I have a class and my validation looks like this:
public ValidationResult Validate(CustomerType customerType)
{
CustomerType Validator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Number).Must(BeUniqueNumber);
return validator.Validate(customerType);
}
public bool BeUniqueNumber(int number)
{
//var result = repository.Get(x => x.Number == customerType.Number)
// .Where(x => x.Id != customerType.Id)
// .FirstOrDefault();
//return result == null;
return true;
}
The CustomerTypeValidator is a basic validator class that validates string properties.
I also add a new rule to check if the number is unique in the db. I do it in this class because there's a reference to the repository. The validator class has no such reference.
The problem here is that the BeUniqueNumber method should have a CustomerType parameter. However when I do this, I get an error on the RuleFor line above because 'Must' needs an int as a parameter.
Is there a way around this?
Can you try this?
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x).Must(HaveUniqueNumber);
return validator.Validate(customerType);
}
public bool HaveUniqueNumber(CustomerType customerType)
{
var result = repository.Get(x => x.Number == customerType.Number)
.Where(x => x.Id != customerType.Id)
.FirstOrDefault();
return result == null;
//return true;
}
You should also be able to do this:
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Number).Must(BeUniqueNumber);
return validator.Validate(customerType);
}
public bool BeUniqueNumber(CustomerType customerType, int number)
{
var result = repository.Get(x => x.Number == number)
.Where(x => x.Id != customerType.Id)
.FirstOrDefault();
return result == null;
//return true;
}
"I also add a new rule to check if the number is unique in the db. I do
it in this class because there's a reference to the repository."
Well, why can't you give your validator a reference to the repository too?
CustomerTypeValidator validator = new CustomerTypeValidator(repository);
Related
I have a method which returns IEnumerable
public static IEnumerable<object> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => new { property = x.Name, value = x.GetValue(obj) })
.Where(x => x.value == null)
.ToList();
return result;
}
Above code will return result as [{"property":"YearOfBirth","value":null}]
I;m now trying to get property valueYearOfBirth from the returned result.
Can someone please suggest/help ?
The type of:
new { property = x.Name, value = x.GetValue(obj) }
is an anonymous type and you can't access fields or properties of that anonymous type outside of the function where it was defined, without using reflection. Here's how you would access its properties using reflection:
foreach (object prop in GetProps(obj))
{
string propertyName = prop.GetType().GetProperty("property").GetValue(prop);
object propertyValue = prop.GetType().GetProperty("value").GetValue(prop);
}
But that's not really the best solution. You don't care about the property value, since you're just returning ones where it's null. So a better solution is IEnumerable<string>:
public static IEnumerable<string> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => new { property = x.Name, value = x.GetValue(obj) })
.Where(x => x.value == null)
.Select(x => x.property)
.ToList();
return result;
}
If you really want to return the property name with its value, I suggest using ValueTuple syntax instead of anonymous types, which will let you access the property and value fields of the ValueTuple (requires C# 7):
public static IEnumerable<(string property, object value)> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => ( x.Name, x.GetValue(obj) ) })
.Where(x => x.Item2 == null)
.ToList();
return result;
}
var yearOfBirth = GetProps(someObject)
.FirstOrDefault(x => x.property == "YearOfBirth")?.value;
Something like that.
You could alternatively just do:
dynamic someObjectDynamic = someObject;
var yearOfBirth = someObjectDynamic.YearOfBirth;
I have this viewModel where I like to check the accessGroupList has any value of True and set baccess base on that value. If they are both false it then baccess would be false but if one of them is true baccess would be true.
MemberViewModel result = new MemberViewModel();
result.IsPractices = true;
result.IsUser = false;
var accessGroupList = new List<string>();
accessGroupList.Add("IsUser");
accessGroupList.Add("IsBestPractices");
var baccess = result.GetType().GetProperties().First(o => o.Name == accessGroupList).GetValue(result, null);
bool? baccess = Property as bool?;
I create this simple console project. You can do this, remove comment from where for using in your project
class Program
{
static void Main(string[] args)
{
var cl = new MyClass();
cl._item1 = false;
cl._item2 = false;
var a = cl.GetType().GetProperties()
//.Where(x => accessGroupList.Contains(x.Name))
.Select(x => new
{
name = x.Name,
value = (bool)x.GetValue(cl, null)
})
.Any(x => x.value);
Console.WriteLine(a);
}
}
public class MyClass
{
public bool _item1 { get; set; }
public bool _item2 { get; set; }
}
First of all note that accessGroupList is list and you need to use Contains or Any to compare it with property name. Then you can select the value of those property that appeared in accessGroupList
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Contains(o.Name))
.Select(t=>(bool)t.GetValue(result, null));
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Any(propName => Equals(propName, o.Name))
.Select(x => (bool)x.GetValue(result, null))
.Any(val => val);
Your problem is that you were using .First (which will only return one item) but then in there, you're also comparing the property name to the list itself. You need to do another linq operation to get the appropriate properties out, then you can check if any of those properties have a value of true
I have a list of errors defined as the following:
List<Errors> test1 = new List<Errors>();
public class Errors
{
public int ID {get; set;}
public int Occurrence {get; set;}
//.....
//.....
}
The errors are unique by the combination of the two fields above.
A second list keeps track of whose been assigned to the errors.
List<Tasks> test2 = new List<Tasks>();
public class Tasks
{
public int ID {get; set;}
public int Occurrence {get; set;}
public int EmployeeID {get; set;}
//.....
}
Also made unique by the same two fields. Essentially the tasks are a subset of the errors that have been assigned to someone.
I would like to use a LINQ query (or equivalent) to determine if the composite ID from the List<Errors> exists in List<Tasks>... To be clear it must use both IDS.
I have found the below solution but have not been able to adopt it to a composite key.
`var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t2.Contains(t1)));`
Just need to use and && operator and check both properties instead of one:
var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t1.ID == t2.ID && t1.Occurance == t2.Occurance);
There is a function for that... Except
var test2NotInTest1 = test1.Except(test2);
If you don't have it you will need to create the interface for equal -- something like this:
var test2NotInTest1 = test1.Except(test2, new ErrorsComparer());
class ErrorsComparer : IEqualityComparer<Errors>
{
public bool Equals(Errors x, Errors y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the products' properties are equal.
return x.ID == y.ID && x.Occurrence == y.Occurrence;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Errors e)
{
if (Object.ReferenceEquals(e, null)) return 0;
int hashID = e.ID == null ? 0 : e.ID.GetHashCode();
int hashO = e.Occurrence.GetHashCode();
//Calculate the hash code for the product.
return hashID ^ hashO;
}
}
You were almost there, just add a correct condition to the LINQ expression:
var test2NotInTest1 = listOfErrors.Where(e => !listOfTasks.Any(t => t.ID == e.Id && t.Occurrence == e.Occurrence)).ToList();
For: to determine if the composite ID from the Errors exists in Tasks...
Another approach is to use Enumerable.Join Method
var assignedErrors =
errors.Join(tasks,
error => new { Id = error.Id, Occurrence = error.Occurrence },
task => new { Id = task.Id, Occurrence = task.Occurrence },
(error, task) => error);
For: to determine if the composite ID from the Errors not exists in Tasks..., as in your sample:
var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t2.Contains(t1)));
You can use HashSet to "speed up" search for already assigned errors.
var assignedErrors = tasks.Select(task => (task.Id, task.Occurrence)).ToHashSet();
var notAssignedErrors =
errors.Where(error => assignedErrors.Contains((error.Id, error.Occurrence)) == false)
.ToList();
Or create your own domain specific extension method:
public static IEnumerable<Errors> NotAssignedIn(
this IEnumerable<Errors> errors,
IEnumerable<Tasks> tasks)
{
var assigned = new HashSet<(int Id, int Occurrence)>();
foreach (var task in tasks)
{
assigned.Add((task.Id, task.Occurrence));
}
foreach (var error in errors)
{
if (assigned.Contains((error.Id, error.Occurrence)) == false)
{
yield return error;
}
}
}
Usage:
var notAssignedErrors = errors.NotAssignedIn(tasks);
I was wondering what was the best approach to compare multiple objects that are created and having the state of the objects changed to Inactive (Deleted), while creating history and dependencies.
This also means im comparing past and present objects inside a relational table (MarketCookies).
Id | CookieID | MarketID
The ugly solution i found was calculating how many objects had i changed.
For this purpose lets call the items of the Past: ListP
And the new items: ListF
I divided this method into three steps:
1 - Count both lists;
2 - Find the objects of ListP that are not present in List F and change their state to Inactive and update them;
3 - Create the new Objects and save them.
But this code is very difficult to maintain.. How can i make an easy code to maintain and keep the functionality?
Market Modal:
public class Market()
{
public ICollection<Cookie> Cookies {get; set;}
}
Cookie Modal:
public class Cookie()
{
public int Id {get;set;}
//Foreign Key
public int CookieID {get;set}
//Foreign Key
public int MarketID {get;set;}
}
Code:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
int ListPCount = ListP.Count();
int ListFCount = ListF.Count();
if(ListPCount > ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete the Object
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Object
}
});
}
else if(ListPCount < ListFCount)
{
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
}
else if(ListPCount == ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
}
}
Without a good, minimal, complete code example that clearly illustrates the question, it's hard to know for sure what even a good implementation would look like, never mind "the best". But, based on your description, it seems like the LINQ Except() method would actually serve your needs reasonably well. For example:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
foreach (var item in ListP.Except(ListF))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP))
{
// create new object
}
}
This of course assumes that your objects have overridden Equals() and GetHashCode(). If not, you can provide your own implementation of IEqualityComparer<T> for the above. For example:
// General-purpose equality comparer implementation for convenience.
// Rather than declaring a new class for each time you want an
// IEqualityComparer<T>, just pass this class appropriate delegates
// to define the actual implementation desired.
class GeneralEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _equals;
private readonly Func<T, int> _getHashCode;
public GeneralEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
_equals = equals;
_getHashCode = getHashCode;
}
public bool Equals(T t1, T t2)
{
return _equals(t1, t2);
}
public int GetHashCode(T t)
{
return _getHashCode(t);
}
}
Used like this:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
IEqualityComparer<Cookie> comparer = new GeneralEqualityComparer<Cookie>(
(t1, t2) => t1.Id == t2.Id, t => t.Id.GetHashCode());
foreach (var item in ListP.Except(ListF, comparer))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP, comparer))
{
// create new object
}
}
I am checking if there are any duplicates while posting list of objects from view to controller by using validation attribute. It works but I would like to know if there is any better approach to follow (may be adding client side validation). Any feedback appreciated.
[AttributeUsageAttribute(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class DuplicateObjectAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value != null)
{
if (value.GetType() == typeof(List<OrdersVM>))
{
List<OrdersVM> objOrdersList = (List<OrdersVM>)value;
if (objOrdersList != null && objOrdersList.Count > 0)
{
if (objOrdersList.Select(p => p.OrderId).Distinct().Count() != objOrdersList.Select(p => p.OrderId).Count())
return false;
}
}
}
return true;
}
}
Is the way you are doing not good enough? I do something similar:
var duplicates = listOfItems
.GroupBy(i => i)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
foreach (var d in duplicates)
;//dosomething
Which is based on the MSDN entry, Find Duplicates using LINQ