I have a following LINQ expression:
var query = entities
.Select(e => new MyObject()
{
Property1 = e.Item1,
Property2 = e.Item2
});
MyObject might have also Property3, Property4 defined. I need to realize which properties are part of LINQ projection via expression visitor.
So I call something like:
var listOfProperties = query.GetSelectedPropertyNames();
and the content of listOfProperties will be string array which contains Property1, Property2 or something by which I can check:
var isPropertyInProjection = query.HasPropertyInProjection(nameof(MyObject.Property3));
and the result will be false.
You can easily do that using an ExpressionVisitor. Just create a new class and override the visiting methods. If you know that the projection was done using member bindings, you can simply override the method VisitMemberBinding and add the bound member to a list that you store as an instance variable. Then all you need to do is to make that instance variable public.
class ProjectionAnalyzer : ExpressionVisitor
{
private HashSet<MemberInfo> boundMembers = new HashSet<MemberInfo>();
protected override MemberBinding VisitMemberBinding(MemberBinding node)
{
boundMembers.Add(node.Member);
return base.VisitMemberBinding(node);
}
public IEnumerable<MemberInfo> BoundMembers => boundMembers;
}
Then, use this class as follows:
var analyzer = new ProjectionAnalyzer();
analyzer.Visit(selectorPredicate);
var boundMembers = analyzer.BoundMembers;
How you obtain the selector predicate depends on your LINQ provider.
I did something similar using VisitMemberAssignment:
namespace BoundPropertiesinQuery
{
static class IEnumerableExtensions
{
class ProjectedVisitor : ExpressionVisitor
{
public IList<string> ProjectedPropertyNames { get; set; } = new List<string>();
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
ProjectedPropertyNames.Add(node.Member.Name);
return base.VisitMemberAssignment(node);
}
}
public static IEnumerable<string> ProjectedProperties<T>(this IQueryable<T> #this)
{
var pv = new ProjectedVisitor();
pv.Visit(#this.Expression);
return pv.ProjectedPropertyNames.Distinct();
}
}
internal class MyObject
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
}
internal class MyOtherObject
{
public int other1 { get; set; }
public int other2 { get; set; }
public int other3 { get; set; }
public int other4 { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var listOfItems = new List<MyOtherObject>()
{
new MyOtherObject
{
other1 = 1,
other2 = 2,
other3 = 3,
other4 = 4
},
new MyOtherObject
{
other1 = 5,
other2 = 6,
other3 = 7,
other4 = 8
}
};
var result = listOfItems.AsQueryable().Select(m => new MyObject
{
Property1 = m.other1,
Property2 = m.other2
}).ProjectedProperties();
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
}
Related
I need to merge data from tables in the database with data based on some logic from third-party sources. I implemented this logic via hashset, for which I overloaded the GetHashCode and Equals methods for entities. Now I don't understand how I can save the result of work in the database via DbSet, with subsequent data loading and subsequent merging (the task of merging/supplementing is periodic)
The directories are quite voluminous, so working through hashset speeds up the process.
class Program
{
private class DummyDbContext { public void SaveChangesAsync() { }}
static void Main(string[] args)
{
var dbContext = new DummyDbContext(); // TODO: Get from DI
// TODO: I don't know how to do it yet with HashSets
var currentFactories = LoadCurrentFactoriesFromDb(dbContext);
var currentProducts = LoadCurrentProductsFromDb(dbContext);
var thirdPartyData = GetThirdPartyData();
foreach (var data in thirdPartyData)
{
/*
In reality, the logic is more complicated, because some data transformation is required.
Some data may be missing. That is why comparing two objects is not quite easy (see the method Product.Equals)
*/
var factory = new Factory(data.otherFactory.Name);
var product = new Product(data.otherProduct.Property1, data.otherProduct.Property2, factory);
if (currentFactories.TryGetValue(factory, out var existedFactory))
factory = existedFactory;
else
currentFactories.Add(factory);
if (currentProducts.TryGetValue(product, out var existedProduct))
{
if (!existedProduct.Factory.Equals(factory))
throw new InvalidOperationException(); // TODO:
product = existedProduct;
factory.Products.Add(product); // TODO:
}
else
currentProducts.Add(product);
}
// **how to implement the saving of combined directories, in hashsets, in the database ?**
dbContext.SaveChangesAsync();
}
private static IEnumerable<(ThirdPartyFactory otherFactory, ThirdPartyProduct otherProduct)> GetThirdPartyData()
{
return new (ThirdPartyFactory otherFactory, ThirdPartyProduct otherProduct)[]
{
( new ThirdPartyFactory () {Name = "SomeFactory"}, new ThirdPartyProduct() {Property1 = "ProductName1"}),
( new ThirdPartyFactory () {Name = "SomeFactory"}, new ThirdPartyProduct() {Property1 = "ProductName2"}),
( new ThirdPartyFactory () {Name = "SomeFactory"}, new ThirdPartyProduct() {Property2 = "Property1"})
};
}
private static HashSet<Factory> LoadCurrentFactoriesFromDb(DummyDbContext context)
{
// DbContext.DbSet<Factory>.GetAll()
return new HashSet<Factory>();
}
private static HashSet<Product> LoadCurrentProductsFromDb(DummyDbContext context)
{
// DbContext.DbSet<Product>.GetAll()
return new HashSet<Product>();
}
}
public class Product
{
public Product(string property1, string property2, Factory factory)
{
Property1 = property1;
Property2 = property2;
Factory = factory;
}
public long Id { get; set; }
public string Property1 { get; }
public string Property2 { get; }
public Factory Factory { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
var product = (Product) obj;
return (string.IsNullOrWhiteSpace(Property1) && string.IsNullOrWhiteSpace(product.Property1)
|| string.CompareOrdinal(this.Property1, product.Property1) == 0)
&& (string.IsNullOrWhiteSpace(Property2) && string.IsNullOrWhiteSpace(product.Property2)
|| string.CompareOrdinal(this.Property2, product.Property2) == 0);
}
public override int GetHashCode()
{
return HashCode.Combine(Property1, Property2).GetHashCode();
}
}
public class Factory
{
public Factory(string name)
{
Name = name;
}
public long Id { get; set; }
public string Name { get; }
public HashSet<Product> Products { get; set; }
}
public class ThirdPartyProduct
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class ThirdPartyFactory
{
public string Name { get; set; }
}
Is it possible to implement this ? Or do I need to convert data from DbSet to HashSet and then back ? But won't I lose information about entities inside the context during such transformations ?
I would like to map the data from one list of objects and another. I am looping through CompanyAEmployee list and able to map FullName and Title. But not able to map Children property.
public class CompanyAEmployee
{
public string FullName { get; set; }
public string Title { get; set; }
public List<CompanyAEmployee> Children { get; set; }
}
public class CompanyBEmployee
{
public string Name { get; set; }
public string PositionName { get; set; }
public List<CompanyBEmployee> Children { get; set; }
}
companyAEmployeeList; // stores all employees of companyA
var companyBEmployeeList = new List<CompanyBEmployee>();
foreach(var employee in companyAEmployeeList)
{
var companyBEmployee = new CompanyBEmployee();
companyBEmployee.Name = employee.FullName;
companyBEmployee.PositionName = employee.Title;
//how to map the children??
}
Can someone suggest a way to map Children?
You can create a recusive method, like below:
public CompanyBEmployee ComAToComB(CompanyAEmployee a){
CompanyBEmployee b = new(){
Name = a.FullName,
PositionName = a.Title,
Children = new()
};
foreach(var child in a.Children){
b.Children.Add(ComAToComB(child));
}
return b;
}
And then call it like
var comB = ComAToComB(comA);
So, in my case it was best solution to use extension methods.
You can see example here
https://dotnetfiddle.net/SwhGMY
Main idea is to use such extension method that was called recursively.
public static class ClassConverterExtensions
{
public static CompanyBEmployee ToCompanyBEmployee(this CompanyAEmployee that)
{
var result = new CompanyBEmployee();
result.Name = that.FullName;
result.PositionName = that.Title;
if(that.Children == null)
{
return result;
}
result.Children = new List<CompanyBEmployee>();
foreach(var item in that.Children)
{
result.Children.Add(item.ToCompanyBEmployee());
}
return result;
}
}
Good point is that you can write this without changing source code of classes CompanyBEmployee and CompanyAEmployee
So, this classes is not referenced one to another, but you can write converters From A to B and from B to A without cyclic references.
Here is the solution:
public class CompanyAEmployee
{
public string FullName { get; set; }
public string Title { get; set; }
public List<CompanyAEmployee> Children { get; set; }
public static explicit operator CompanyBEmployee(CompanyAEmployee employee)
{
CompanyBEmployee employee1 = new CompanyBEmployee();
employee1.Name = employee.FullName;
employee1.PositionName = employee.Title;
employee1.Children = new List<CompanyBEmployee>(employee.Children.Count);
int count = employee.Children.Count;
for (int i = 0; i < count; i++)
employee1.Children[i] = (CompanyBEmployee)employee.Children[i];
return employee1;
}
}
public class CompanyBEmployee
{
public string Name { get; set; }
public string PositionName { get; set; }
public List<CompanyBEmployee> Children { get; set; }
}
Now you can just use an assignment operator with explicit casting from CompanyAEmployee to CompanyBEmployee.
Like this:
CompanyAEmployee employee = new CompanyAEmployee();
//... assign all the fields
CompanyBEmployee employee2 = (CompanyBEmployee)employee.
Now you are done!
I'm having problems trying to get Should().BeEquivalentTo() to work with types that derive from a base class and implement a collection interface:
public class Entity
{
public string Id {get; set;}
public string Name {get; set;}
}
public class Derived : Entity, ICollection<Entity>
{
private List<Entity> m_Children = new List<Entity>();
public string Description { get; set; }
public int Count => ((ICollection<Entity>)m_Children).Count;
public bool IsReadOnly => ((ICollection<Entity>)m_Children).IsReadOnly;
public void Add(Entity item)
{
((ICollection<Entity>)m_Children).Add(item);
}
public void Clear()
{
((ICollection<Entity>)m_Children).Clear();
}
public bool Contains(Entity item)
{
return ((ICollection<Entity>)m_Children).Contains(item);
}
public void CopyTo(Entity[] array, int arrayIndex)
{
((ICollection<Entity>)m_Children).CopyTo(array, arrayIndex);
}
public IEnumerator<Entity> GetEnumerator()
{
return ((ICollection<Entity>)m_Children).GetEnumerator();
}
public bool Remove(Entity item)
{
return ((ICollection<Entity>)m_Children).Remove(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((ICollection<Entity>)m_Children).GetEnumerator();
}
}
The Test
[TestMethod]
public void EquivalenceTest()
{
var expected = new Derived
{
Id = "123",
Name = "abc",
Description = "def"
};
var actual = new Derived
{
Id = "121",
Name = "xyz",
Description = "def"
};
actual.Should().BeEquivalentTo(expected); // This succeeds, but should fail
}
The call to BeEquivalentTo seems to be ignoring the properties that are defined in the object, and only treating the object as a collection.
How can I get the framework to check the properties and the contents of the collection?
Edit
It seems like this is a known issue
Does anyone know of a workaround?
It's a known issue when comparing classes that implements IEnumerable and have extra properties to be compared.
Here's a way to hack the comparison.
public class Entity : IEnumerable<int>
{
private int[] ints = new[] { 1 };
public int Id { get; set; }
public string Name { get; set; }
public IEnumerator<int> GetEnumerator() => ((IEnumerable<int>)ints).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<int>)ints).GetEnumerator();
}
[TestMethod]
public void EquivalenceTest()
{
var expected = new Entity
{
Id = 1,
Name = "abc",
};
var actual = new Entity
{
Id = 1,
Name = "abc",
};
actual.Should().BeEquivalentTo(expected, opt => opt
.Using<Entity>(e =>
e.Subject.Should().Match<Entity>(f => f.Name == e.Expectation.Name)
.And.Subject.Should().Match<Entity>(f => f.Id == e.Expectation.Id)
.And.Subject.Should().BeEquivalentTo(e.Expectation)
)
.WhenTypeIs<Entity>());
}
I want to compare all of my list of generic T property value to my local variable searchText.
i already try:
x.GetType().GetProperties().All(props => props.GetValue(x).ToString().ToLower().Contains(searchText.ToLower()))
and i get error : NullReferenceException: Object reference not set to an instance of an object.
here my full method :
protected List<T> ProcessCollection<T>(List<T> lstElements, IFormCollection requestFormData, Func<T, IComparable> getProp)
{
string searchText = string.Empty;
Microsoft.Extensions.Primitives.StringValues tempOrder = new[] { "" };
if (requestFormData.TryGetValue("search[value]", out tempOrder))
{
searchText = requestFormData["search[value]"].ToString();
}
var skip = Convert.ToInt32(requestFormData["start"].ToString());
var pageSize = Convert.ToInt32(requestFormData["length"].ToString());
if (requestFormData.TryGetValue("order[0][column]", out tempOrder))
{
var columnIndex = requestFormData["order[0][column]"].ToString();
var sortDirection = requestFormData["order[0][dir]"].ToString();
tempOrder = new[] { "" };
if (requestFormData.TryGetValue($"columns[{columnIndex}][data]", out tempOrder))
{
var columName = requestFormData[$"columns[{columnIndex}][data]"].ToString();
if (pageSize > 0)
{
var prop = GetProperty<T>(columName);
if (sortDirection == "asc")
{
return lstElements.Where(x=>x.GetType().GetProperties().All(props => props.GetValue(x).ToString().ToLower().Contains(searchText.ToLower()))
.Skip(skip).Take(pageSize).OrderBy(b => prop.GetValue(b)).ToList();
}
else
return lstElements
.Where(x => getProp(x).ToString().ToLower().Contains(searchText.ToLower()))
.Skip(skip).Take(pageSize).OrderByDescending(b => prop.GetValue(b)).ToList();
}
else
return lstElements;
}
}
return null;
}
this is how i call my method :
var listItem = ProcessCollection<Jtabel>(temp,requestFormData,x=>x.Name);
the temp that i pass to method already filled
and this is the class type that i pass to this method
public class Jtabel : BaseModel
{
public string Creator { get; set; }
public string Name { get; set; }
public int SO { get; set; }
public int SOD { get; set; }
public int SAP { get; set; }
public int BA { get; set; }
public int Invoice { get; set; }
public int EPD { get; set; }
public double DP { get; set; }
}
i will try to explain more detail as much i can, so i want to get all of my property from Jtabel class to my ProcessCollection where inside the method all of Jtable property will test each element that's contain searchText.
I am attempting to use ProtoBuf net to serialize an object tree with the classes in the following format:
[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
interface IBeast
{
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
using (var fs = File.Create(#"c:\temp\protobuftest.bin"))
{
Serializer.Serialize(fs, collectionList);
fs.Close();
}
}
private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(cat)},
{new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>() {
specialCollection1, specialCollection2 };
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}
Notice how the class I am serializing (MySpecialCollectionList<T>) is derived from a List<SomeOtherClass<T>>, not just List<T>.
I am struggling to work out where to put "ProtoInclude" attributes to get this to serialize all the items in the MySpecialCollectionList. Any help would be much appreciated.
Inheritance is not an issue here since even if A : B it is not true that Foo<A> : Foo<B>. Note that protobuf-net won't use a non-default constructor, although it is possible to skip the constructor, binding to the field directly (even readonly). While you may have 6 T, I can't see (from the code) that it would ever be in doubt which closed type you intend, and if the closed type is known you should be set.
If you have a Foo<SomeBaseClass> and a number of concrete types inherited from SomeBaseClass then the markers would o on SomeBaseClass.
However, if you have a concrete scenario I can use to reproduce your issue, I'll happily take a look.
Updated re edit:
There are a couple of key points drawn out in the example:
in common with most binding APIs, XmlSerializer and IIRC DataContractSerializer, an item is either a list xor an item with values; if a collection (something implementing IList) has properties itself, they will not be serialized; encapsulation is preferred over inheritance here, i.e. something that has a Name and has a list (rather than has a Name and is a list)
protobuf-net v1 does not support interface-based serialization; v2 does, but as with XmlSerializer and DataContractSerializer you need to explicitly tell it what things it needs to expect; quite nicely, though, we can move the [ProtoMember] onto the interface itself
Here's a fully working version in v2:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
[ProtoMember(2)]
public List<MySpecialCollection<T>> Items { get { return items; } }
}
[ProtoContract]
class MySpecialCollection<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<Special<T>> items = new List<Special<T>>();
[ProtoMember(2)]
public List<Special<T>> Items { get { return items; } }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
public string Name { get; set; }
}
public static class Form1
{
private static void Main()
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
var copy = Serializer.DeepClone(collectionList);
}
private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
{new Special<IBeast>(ant),
new Special<IBeast>(cat),
new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>()
{
Items =
{new Special<IBeast>(ant),
new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>()
{
Items ={
specialCollection1, specialCollection2 }
};
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}