How can I compare two classes base their methods? - c#

I want to find out the missing properties in one class by comparing the other class
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserDTO
{
public string UserName { get; set; }
public string FirstName { get; set; }
}
Above I should get the output like "UserID, "LastName" properties are missing in UserDTO.

var list = typeof(User).GetProperties().Select(x => x.Name)
.Except(typeof(UserDTO).GetProperties().Select(y => y.Name))
.ToList();
EDIT
Including suggestions in comments and public Fields
public static IEnumerable<string> Diff(Type t1, Type t2)
{
return t1.GetProperties().Select(p1 => new { Name = p1.Name, Type = p1.PropertyType })
.Concat(t1.GetFields().Select(f1 => new { Name = f1.Name, Type = f1.FieldType }))
.Except(t2.GetProperties().Select(p2 => new { Name = p2.Name, Type = p2.PropertyType })
.Concat(t2.GetFields().Select(f2 => new { Name = f2.Name, Type = f2.FieldType })))
.Select(a => a.Name);
}

Use reflection to get the properties, see Type.GetProperties. Then compare both property lists to find the missing ones.
var UserProperties = typeof(User).GetProperties().Select(p => p.Name);
var UserDTOProperties = typeof(UserDTO).GetProperties().Select(p => p.Name);
var missingProperties = UserProperties.Except(UserDTOProperties);
Take into account that all inherited properties will also be present in these lists, unless yous specify BindingFlags.DeclaredOnly to the GetProperties() method, see BindingFlags.

Related

Error when using Select() instead of Include() in a query

I have the following query:
var catInclude = _db.Cat
.Where(x => x.ProvId == request.ProvId)
.Include(x => x.CatItems)
.SingleOrDefault(p => p.Id == request.ProvId
cancellationToken: cancellationToken);
As I don't want to get all properties from CatItems with Include(), I have created the following query:
var catSelect = _db.Cat
.Where(x => x.ProvId == request.ProvId)
.Select(p ==> new
{ Provider = p,
Items = p.CatItems.Select(x => new List<CatItems> { new CatItems
{ Id = x.Id, Name = x.Name, Price = x.Price } }
})})
SingleOrDefault(cancellationToken: cancellationToken);
But something is wrong in the 2nd query because here return _mapper.ProjectTo<CatDto>(cat) I get the following error:
Argument 1: cannot convert from '<anonymous type: Db.Entities.Cat Prov, System.Colletions.Generic.IEnumerable<System.Colletions.Generic.List<Models.CatItems> > Items>' to 'System.Linq.IQueryable'
Here is my CatDto:
public class CatDto
{
public int ProvId { get; set; }
public List<CatItems> CatItems { get; set; }
}
Here are my entities:
public class Prov
{
public int Id { get; set; }
public Cat Cat { get; set; }
}
public class Cat
{
public int Id { get; set; }
public int ProvId { get; set; }
public List<CatItems> CatItems { get; set; }
}
public class CatItems
{
public int Id { get; set; }
public int CatId { get; set; }
public DateTime CreatedOn { get; set; }
}
Is there a way to recreate the 2nd query and use it?
Main difference that instead of returning List of CatItems, your code returns IEnumerable<List<CatItems>> for property Items.
So, just correct your query to project to List:
var catSelect = await _db.Cat
.Where(x => x.ProvId == request.ProvId)
.Select(p => new CatDto
{
ProvId = p.ProvId,
Items = p.CatItems.Select(x => new CatItems
{
Id = x.Id,
Name = x.Name,
Price = x.Price
})
.ToList()
})
.SingleOrDefaultAsync(cancellationToken: cancellationToken);
I mean, even the exception is pretty self-explanatory. Nevertheless:
You are performing a .Select(...). It returns an Anonymous type. So, your catSelect is an anonymous type, thus the AutoMapper fails.
The quickest fix is to just cast (Cat)catSelect before mapping.
Or, you can dig deeper into how does AutoMapper play with anonymous types.
I feel like you can make most of the classes inherent Id and why is public cat CAT {get; set;} i thought you were supposed to initialize some kind of value

LINQ with dynamic group by

I have model:
public class Student
{
public string Name{ get; set; }
public DateTime BirthDate{ get; set; }
public string UniversityName{ get; set; }
public decimal Balance{ get; set; }
}
I have three bool variables:
IsName
IsBirthDate
IsUniversityName
And based on them, I need to create GroupBy. If IsBirthDate=true then
DbContext.Students.GroupBy(x => new { x.BirthDate }).Select(s => new
{
s.Key.BirthDate,
Balance = s.Sum(x => x.Balance).ToString(),
});
If IsBirthdate=true, IsUniversityName=true then
DbContext.Students.GroupBy(x => new { x.BirthDate, x.UniversityName }).Select(s => new
{
s.Key.BirthDate,
s.Key.UniversityName,
Balance = s.Sum(x => x.Balance).ToString(),
});
And other options with bool parameters.
How to generate the query dynamically with .GroupBy and .Select?
Maybe this helps you: Create a class that represents the key for your GroupBy:
public class StudentGroupingKey
{
public string Name { get; set; }
public DateTime? BirthDate{ get; set; }
public string UniversityName { get; set; }
}
If IsName is true, the Name property of the grouping key will be the value, otherwise it should have always the same value (e.g. null). In your Select method, you have to have always every property, but they will be null if the corresponding properties will be false. If it is necessary for you that these properties do not exist, you can't work with anoymous types. Maybe dynamic might be an option in that case.
DbContext.Students.GroupBy(x => new StudentGroupingKey
{
Name = IsName ? x.Name : null,
BirthDate = IsBirthDate ? x.Birthdate : null,
UniversityName = IsUniversityName ? x.UniversityName : null
}).Select(s => new
{
s.Key.BirthDate,
s.Key.Name,
s.Key.UniversityName,
Balance = s.Sum(x => x.Balance).ToString()
});

How to initialize a property by setting some value without passing anything over a constructor

I have some additional work to do get the value from a string and set to a property As shown in the example.
Here I am putting all the logic to extract a value from a string in a constructor and I have set all the property.
public class CourseModuleView
{
public CourseModuleView(string str)
{
var re = new Regex(#"'(.*?)'");
var results = Regex.Matches(str, #"'(.*?)'", RegexOptions.Singleline)
.Cast<Match>()
.Select(x => x.Groups[1].Value);
UserId = int.Parse(results.ElementAt(0));
ComponentType = results.ElementAt(1);
ModuleId = int.Parse(results.ElementAt(2));
}
public int Id { get; set; }
public int UserId { get; set; }
public int ModuleId { get; set; }
public string ComponentType { get; set; }
}
I am calling my constructor as:
//str will have the pattern like this,
//"The user with id '2209' viewed the 'url' activity with course module id '317'
// but it will be a list of string
var modCompletedResult = allLog
.Where(x => x.EventName == EventNameMessage.CourseModuleView)
.Select(x => new CourseModuleView(x.str)
{
//other property setting code goes here
}).ToList();
Sometimes if I want to inject other value in the same constructor (from the different methods) I have to pass both the unrelated value also.
What is the other most appropriate way to set the property while extracting the value from the string without writing all the logic in the constructor?
public int Id { get; set { this.Id = extractId(value); }}
public int UserId { get; set { this.UserId = extractUserId(value); }}
.....
Then assign it in the select:
var theString = "The user with id '2209' viewed the 'url' activity with course module id '317'.";
var modCompletedResult = allLog
.Where(x => x.EventName == EventNameMessage.CourseModuleView)
.Select(x => new CourseModuleView()
{
Id = theString,
UserId = theString,
......
}).ToList();
Sorry im using a phone, quite hard to write code here.
The C# setter and getter is powerful, you can even set other properties when assignning only one property. Example
public int Id { get;
set {
var extracted = extractAll(value);
this.Id = extracted.Id;
this.UserId = extracted.UserId;
.......
}
}
.Select(x => new CourseModuleView()
{
Id = theString, //assign only Id will automatically assign all properties according to the Id setter logic
}).ToList();
Btw you have to keep the name value in the setter, if not, it will try to find the named variable inside the local scope.
Sorry my mistake, when you assigning property in select, it has to be the same type, Id should be assigned by int.
For your case, you can make a dummy property of type string then apply the setter logic in it.
Example:
public string dummy { get; set { ..... }}
Hope this works for you.
public void SetEverything(string str)
{
var re = new Regex(#"'(.*?)'");
var results = Regex.Matches(str, #"'(.*?)'", RegexOptions.Singleline)
.Cast<Match>()
.Select(x => x.Groups[1].Value);
this.UserId = int.Parse(results.ElementAt(0));
this.ComponentType = results.ElementAt(1);
this.ModuleId = int.Parse(results.ElementAt(2));
}
public int Id { get; set; }
public int UserId { get; set; }
public int ModuleId { get; set; }
public string ComponentType { get; set; }
public string Setter { get { return ""; } //return empty string
set {
SetEverything(value);
}
}
Then
var modCompletedResult = allLog
.Where(x => x.EventName == EventNameMessage.CourseModuleView)
.Select(x => new CourseModuleView()
{
Setter = x.str,
}).ToList();

How to create a dictionary of field values/counts from an observable collection?

I have an ObservableCollection<CustomerModel> Customers, that holds a Country field. What I want to do is, create an observable collection of type PiePointModel. In order to store the country name and number of occurrences of that country name.
So I set up an ObservableCollection<PiePointModel> CountryRatioCollection, where PiePoint holds a name and amount.
Then I tried to assign that collection to my Customers, by converting it to a dictionary holding the required values:
CountryRatioCollection = new ObservableCollection<PiePointModel>();
CountryRatioCollection = Customers.GroupBy(i => i.Country).ToDictionary(g => g.Key, g => g.Count());
But I get an error stating that this can't be implicitly converted:
Error 2 Cannot implicitly convert type 'System.Collections.Generic.Dictionary<string,int>' to 'System.Collections.ObjectModel.ObservableCollection<MongoDBApp.Models.PiePointModel>'
I understand that this is because the Dictionary type is not the same as my PiePoint model class.
Can anyone offer advice on making query and conversion?
This is the PiePoint class for reference, that holds the name and amount:
public class PiePointModel
{
public string Name { get; set; }
public int Amount { get; set; }
}
And this is the CustomerModel that holds the country field:
public class CustomerModel
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("firstName")]
public string FirstName { get; set; }
[BsonElement("lastName")]
public string LastName { get; set; }
[BsonElement("email")]
public string Email { get; set; }
[BsonElement("address")]
public string Address { get; set; }
[BsonElement("country")]
public string Country { get; set; }
public override string ToString()
{
return Country;
}
}
You should use Select (not ToDictionary) and create PiePointModel for each group.
IEnumerable<PiePointModel> piePoints = Customers.GroupBy(i => i.Country).Select(s => new PiePointModel()
{
Name = s.Key,
Amount = s.Count()
});
CountryRatioCollection = new ObservableCollection<PiePointModel>(piePoints);
Also notice that I used: CountryRatioCollection = new ObservableCollection<PiePointModel>(..) because CountryRatioCollection is of type ObservableCollection and you cannot assign here dictionary like in your example.
Constructor of ObservableCollection<T> can take IEnumerable<T> - I used it here.
Other way is use loop and add new PiePointModel to collection
CountryRatioCollection = new ObservableCollection<PiePointModel>();
var groups = Customers.GroupBy(i => i.Country);
foreach(var gr in groups)
{
PiePointModel piePointModel = new PiePointModel()
{
Name = gr.Key,
Amount = gr.Count()
};
CountryRatioCollection.Add(piePointModel);
}

LINQ - append MemberBinding expression into exist MemberInit expression

Basic idea is similar to Merging Expression Trees to Reuse in Linq Queries.
In my situation, I have two models and DTOs:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Extra Extra { get; set; }
}
public class Extra
{
public int Id { get; set; }
public string Text { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public ExtraDto Extra { get; set; }
}
public class ExtraDto
{
public int Id { get; set; }
public string Text { get; set; }
}
and expressions:
Expression<Func<Extra, ExtraDto>> extraSelector = o => new ExtraDto
{
Id = o.Id,
Text = o.Text
};
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name
};
Now, I'd like to 'append' extraSelector into userSelector. The pseudo code is like:
var selectorExpression = userSelector.Append(user => user.Extra, extraSelector);
Context.Users.Select(selectorExpression).ToList();
The final expression would be like this:
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name,
Extra = new ExtraDto
{
Id = o.Extra.Id,
Text = o.Extra.Text
}
};
I've tried using ExpressionVisitor, but no luck.
Apart from the "merge" of the two selectors, you have to insert the "path" o => o.Extra into the extraSelector and create a new "bind expression" for the property Extra of UserDto.
In fact, i'm playing around with such scenarios within this project, where i've tried to abstract this kind of expression plumbing. Your "merge" would then look like that:
userSelector = extraSelector.Translate()
.Cross<User>(o => o.Extra)
.Apply(o => o.Extra, userSelector);
The Translate extension method is just a little helper to make use of type inference, Cross inserts o => o.Extra into the extraSelector, Apply creates the "bind expression" for the property Extra of UserDto and finally merges the result with userSelector.

Categories