map common properties in separate method using generics - c#

I have a class MechanicalData and in that i have list of objects and in the below function i am trying to form list of objects with values coming from input.
Mechanicaldata class looks like below
public class MechanicalData
{
public List<LibraryA170> AirflowsA170 { get; set; }
......
}
and the classes libraryA170 and LibraryAcoustic looks like as follows
public class LibraryA170 : AEIMasterBase
{
public string Category { get; set; }
public string SpaceFunction { get; set; }
[Column(TypeName = "varchar(32)")]
public DirectExhaust? DirectExhaust { get; set; }
.....
......
}
public class LibraryAcoustic : AEIMasterBase
{
public string Category { get; set; }
public string SpaceFunction { get; set; }
public double? NoiseCriteria { get; set; }
.......
}
and base class AEIMasterBase looks like as below
public class AEIMasterBase
{
public Guid Id { get; set; }
public MasterSection MasterSection { get; set; }
public List<string> NotesHTML { get; set; }
public bool? IsApproved { get; set; }
public Guid? InitialRevisionId { get; set; }
public Guid? LatestRevisionId { get; set; }
public int? Revision { get; set; }
}
Below i am trying to map all those fields with LINQ select
private static MechanicalData TransformedMechanicalData(MechanicalData sourceMechanicalData, Dictionary<string, MasterSection> masterSectionMappedLibrary)
{
return new MechanicalData
{
AirflowsA170 = sourceMechanicalData.AirflowsA170.Select(airflow170 => new LibraryA170
{
Id = airflow170.Id,
InitialRevisionId = airflow170.InitialRevisionId,
LatestRevisionId = airflow170.LatestRevisionId,
IsApproved = true,
Revision = airflow170.Revision,
NotesHTML = airflow170.NotesHTML,
SpaceFunction = airflow170.SpaceFunction,
Category = airflow170.Category,
MasterSection = masterSectionMappedLibrary["Library A170"],
........
}).ToList(),
Acoustic = sourceMechanicalData.Acoustic.Select(acoustic => new LibraryAcoustic
{
Id = acoustic.Id,
InitialRevisionId = acoustic.InitialRevisionId,
LatestRevisionId = acoustic.LatestRevisionId,
IsApproved = true,
Revision = acoustic.Revision,
NotesHTML = acoustic.NotesHTML,
Category = acoustic.Category,
SpaceFunction = acoustic.SpaceFunction,
......
}).ToList()
};
}
is there any way i can pass two objects to a method and map common properties inside that method and leave uncommon properties to be mapped inside the select statement while adding to the list.
I am looking for something like as below if possible
public static class ItemExtensionMethods
{
public static readonly Expression<Func<Item, MinimalItem>> MapToMinimalItemExpr =
source => new MinimalItem
{
Id = source.Id, // only common properties like id, revision, IsApproved
Property1 = source.Property1
};
}
below are some common properties
Id = airflow170.Id,
InitialRevisionId = airflow170.InitialRevisionId,
LatestRevisionId = airflow170.LatestRevisionId,
.......
Could any one please suggest any idea on this, Many thanks in advance
update getting below error

Given two expression tree's, you want to locate the two MemberInitExpression nodes, merge their MemberBinding's and swap any ParameterExpression.
class LocateBindings : ExpressionVisitor
{
public IEnumerable<MemberBinding> Bindings { get; private set; }
protected override Expression VisitMemberInit(MemberInitExpression node)
{
Bindings = node.Bindings;
return base.VisitMemberInit(node);
}
}
class MergeBindings : ExpressionVisitor
{
private IEnumerable<MemberBinding> bindings;
private ParameterExpression parameter;
public MergeBindings(IEnumerable<MemberBinding> bindings, ParameterExpression parameter)
{
this.bindings = bindings;
this.parameter = parameter;
}
protected override Expression VisitMemberInit(MemberInitExpression node)
=> node.Update(node.NewExpression,
node.Bindings.Concat(bindings)
.Select(VisitMemberBinding));
protected override Expression VisitParameter(ParameterExpression node)
=> parameter;
}
public static Expression<Func<P, D>> Merge<BP, P, B, D>(
Expression<Func<BP, B>> baseExpr,
Expression<Func<P, D>> derivedExpr
)
where D:B where P:BP
{
var locate = new LocateBindings();
locate.Visit(baseExpr);
var merge = new MergeBindings(locate.Bindings, derivedExpr.Parameters[0]);
return merge.VisitAndConvert(derivedExpr, "");
}
For example;
Expression<Func<[insert type], AEIMasterBase>> baseExpression = basearg => new AEIMasterBase
{
Id = basearg.Id,
InitialRevisionId = basearg.InitialRevisionId,
LatestRevisionId = basearg.LatestRevisionId,
IsApproved = true,
Revision = basearg.Revision,
NotesHTML = basearg.NotesHTML,
SpaceFunction = basearg.SpaceFunction,
};
Expression<Func<[insert type], LibraryA170>> derivedExpression = airflow170 => new LibraryA170
{
Category = airflow170.Category,
MasterSection = masterSectionMappedLibrary["Library A170"],
};
var merged = Merge(baseExpression, derivedExpression);
...
AirflowsA170 = sourceMechanicalData.AirflowsA170.Select(merged).ToList()
...

Related

C# reflection get all property information inside the proerperties with custom attribute

I am writing a method for extracting all properties from an object (including properties of its own) with custom attribute . For example
public class SomeModel
{
[Custom]
public string Name { get; set; }
public string TestData { get; set; }
[Custom]
public string Surname { get; set; }
public InnerModel InnerModel { get; set; }
}
And Inner Model :
public class InnerModel
{
public string Id { get; set; } = "TestID";
[Custom]
public string Year { get; set; }
public ThirdObject HidedObject { get; set; }
}
And the third one :
public class ThirdObject
{
[Custom]
public string HidedName { get; set; }
}
I need to find all properties with "Custom" attribute .
Testing :
SomeModel model = new SomeModel()
{
Name = "farid",
Surname = "Ismayilzada",
TestData = "Test" ,
InnerModel = new InnerModel() { Year ="2022" , HidedObject= New ThirdObject{ HidedName="Secret"}}
};
I need to write the method
GetMyProperties(model) => List<PropInf>()
[PropertyName= Name,Value=Farid ,Route="Name" ]
[PropertyName= Surname,Value=Ismayilzada,Route="Surname" ]
[PropertyName= Year,Value=2022,Route="InnerModel.Year" ]
[PropertyName= HidedName,Value=Secret,Route="InnerModel.HidedObject.HidedName" ]
How to get this information ?
You can write a method like this :
private static IEnumerable<PropInfo> GetPropertiesInfo(object obj, string route = "")
{
List<PropInfo> results = new List<PropInfo>();
// You can filter wich property you want https://learn.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo?view=net-6.0
var objectProperties = obj.GetType().GetProperties().Where(p => p.CanRead);
foreach (var property in objectProperties)
{
var value = property.GetValue(obj);
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
results.AddRange(GetPropertiesInfo(value, route + property.Name + "."));
}
else
{
// Check if the property has the Custom Attribute
var customAttributes = property.GetCustomAttributes<CustomAttribute>();
if (!customAttributes.Any())
continue;
// You can set a method in your Attribute : customAttributes.First().CheckIfNeedToStoreProperty(obj);
results.Add(new PropInfo()
{
PropertyName = property.Name,
Value = value,
Route = route + property.Name
});
}
}
return results;
}
public class PropInfo
{
public string PropertyName { get; set; }
public object Value { get; set; }
public string Route { get; set; }
}
public class CustomAttribute : Attribute
{
public bool CheckIfNeedToStoreProperty(object obj)
{
return true;
}
}

C# Generic search in object recursively [duplicate]

This question already has answers here:
Recursively Get Properties & Child Properties Of A Class
(5 answers)
Closed 2 years ago.
I am trying to write an universal search to use for all objects.
I have this code, which is working fine to search in just one object's properties, but I would also like to search also in properties in related objects.
Eg. I have these Models/Objects
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address{ get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract
{
public int Id { get; set; }
public DateTime From{ get; set; }
public DateTime To{ get; set; }
public string Comment{ get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
and I want to search if any of properties contains some a string eg. "Peter", I will call it this way:
string searchString = "Peter";
var customers = db.Customers
.Include(x => x.Contracts)
.WhereAnyPropertiesOfSimilarTypeContains(searchString);
this code will check if any properties of 'Customer' contains string "Peter".
But I would also need to check if the related model 'Contract' contains "Peter.
public static class EntityHelper
{
public static IQueryable<TEntity> WhereAnyPropertiesOfSimilarTypeContains<TEntity, TProperty>(this IQueryable<TEntity> query, TProperty value)
{
var param = Expression.Parameter(typeof(TEntity));
var predicate = PredicateBuilder.False<TEntity>(); //--- True to equal
var entityFields = GetEntityFieldsToCompareTo<TEntity, TProperty>();
foreach (var fieldName in entityFields)
{
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var predicateToAdd = Expression.Lambda<Func<TEntity, bool>>(
Expression.Call(
Expression.PropertyOrField(param, fieldName), method,
Expression.Constant(value)), param);
predicate = predicate.Or(predicateToAdd); //--- And to equal
}
return query.Where(predicate);
}
// TODO: You'll need to find out what fields are actually ones you would want to compare on.
// This might involve stripping out properties marked with [NotMapped] attributes, for
// for example.
public static IEnumerable<string> GetEntityFieldsToCompareTo<TEntity, TProperty>()
{
Type entityType = typeof(TEntity);
Type propertyType = typeof(TProperty);
var fields = entityType.GetFields()
.Where(f => f.FieldType == propertyType)
.Select(f => f.Name);
var properties = entityType.GetProperties()
.Where(p => p.PropertyType == propertyType)
.Select(p => p.Name);
return fields.Concat(properties);
}
}
Thanks.
After reread the question. I don't know what are you trying, but here I put the idea I have what are you looking for.
public class Customer : AbstractEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract : AbstractEntity
{
//what property here can be string "Peter"? Comments?
//what are you trying?
public int Id { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public string Comment { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
public abstract class AbstractEntity
{
//this method can be used to preselect properties you want
protected virtual Tuple<bool, ICollection<PropertyInfo>> PropertyCollector()
{
return new Tuple<bool, ICollection<PropertyInfo>>(false, null);
}
public IEnumerable<Tuple<Type, object>> GetRowValues()
{
foreach (var prop in GetRows())
{
yield return new Tuple<Type, object>(prop.PropertyType, prop.GetValue(this));
}
}
public ICollection<PropertyInfo> GetRows()
{
var tuple = PropertyCollector();
ISet<PropertyInfo> pInfo;
if (tuple.Item1)
{
pInfo = new HashSet<PropertyInfo>(tuple.Item2);
}
else //search all non virtual, private, protected properties, "following POCO scheme"
{
pInfo = new HashSet<PropertyInfo>();
foreach (var prop in GetType().GetProperties())
{
foreach (var access in prop.GetAccessors())
{
if ((!access.IsVirtual && !access.IsPrivate) && (prop.CanWrite && prop.CanRead))
{
pInfo.Add(prop);
}
}
}
}
return pInfo;
}
}
public static class Searchs
{
public static ICollection<object> ObjectsWithStringFound(ICollection<Customer> customers, string toBeFound)
{
var objs = new List<object>();
foreach (var cust in customers)
{
var strings = cust.GetRowValues().Where(tpl => tpl.Item1 == typeof(string)).Select(tpl => tpl.Item2);
var contracts = cust.GetRowValues().Where(tpl => tpl.Item2 is IEnumerable<Contract>).Select(tpl => tpl.Item2);
if (strings.Any(str => str == toBeFound))
{
objs.Add(cust);
}
else if (contracts.Any(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound))
{ //What I suppose I must "match" with "Peter"??!?!
objs.Add(contracts.First(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound));
}
}
return objs;
}
}
I think we aren't understanding each other.

How to fill ObservableCollection from two tables from DataBase?

Trying to populate an ObservableCollection from a database using the Entity Framework. Everything was fine until I started working with linked tables.
I created the DeviceCategory and DeviceComplexity model, and now in the WyeModel I try to integrate them into the DeviceCategoryViewModel. Further, in DeviceCategoryViewModel, I indicated a request for taking information from the database, but I ran into a problem. How to fill in ObservableCollection with this information? I tried different ways, but it didn’t lead to anything, I just got more confused.
DeviceCategoriesViewModel
class DeviceCategoryViewModel
{
TechDContext dc = new TechDContext();
public int Device_category_id { get; set; }
public string Device_category_name { get; set; }
public int Device_complexity_id { get; set; }
public string Device_complexity_name { get; set; }
public static DeviceCategoryViewModel DeviceCaterogyVM(DeviceCategory deviceCategory, DeviceComplexity deviceComplexity)
{
return new DeviceCategoryViewModel
{
Device_category_id = deviceCategory.Device_category_id,
Device_category_name = deviceCategory.Category_name,
Device_complexity_id = deviceCategory.Device_complexity_id,
Device_complexity_name = deviceComplexity.Device_complexity_name
};
}
public void FillDeviceDategories()
{
var q = from cat in dc.DeviceCategories
join com in dc.DeviceComplexities on cat.Device_complexity_id equals com.Device_complexity_id
select new
{
Device_category_id = cat.Device_category_id,
Category_name = cat.Category_name,
Device_complexity_id = com.Device_complexity_id,
Device_complexity_name = com.Device_complexity_name
};
items = q;
deviceCategories = Convert(items);
}
public ObservableCollection<DeviceCategoryViewModel>
Convert(IEnumerable<object> original)
{
return new ObservableCollection<DeviceCategoryViewModel>(original.Cast<DeviceCategoryViewModel>());
}
private IEnumerable<object> items;
public IEnumerable<object> Items
{
get
{
return items;
}
}
private ObservableCollection<DeviceCategoryViewModel> deviceCategories;
public ObservableCollection<DeviceCategoryViewModel> DeviceCategories
{
get
{
FillDeviceDategories();
return deviceCategories;
}
}
DeviceCategory Model
[Table("device_categories")]
public class DeviceCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Device_category_id { get; set; }
public string Category_name { get; set; }
//[ForeignKey]
public int Device_complexity_id { get; set; }
public DeviceCategory()
{
}
public DeviceCategory(string name, int complexity_id)
{
Category_name = name;
Device_complexity_id = complexity_id;
}
}
DeviceCompexity Model
[Table("device_complexities")]
public class DeviceComplexity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Device_complexity_id { get; set; }
public string Device_complexity_name { get; set; }
public DeviceComplexity()
{
}
public DeviceComplexity(string name)
{
Device_complexity_name = name;
}
}
I now get an error in the conversion method
You'd try to cast your LINQ query result to ObservableCollection<DeviceCategoryViewModel> in separate Convert function.
Why not to directly collect your LINQ query result to ObservableCollection<DeviceCategoryViewModel>
Just use like this
var q = from cat in dc.DeviceCategories
join com in dc.DeviceComplexities on cat.Device_complexity_id equals com.Device_complexity_id
select new DeviceCategoryViewModel // <= Note This Line
{
Device_category_id = cat.Device_category_id,
Category_name = cat.Category_name,
Device_complexity_id = com.Device_complexity_id,
Device_complexity_name = com.Device_complexity_name
};
deviceCategories = new ObservableCollection<DeviceCategoryViewModel>(q);
OR if you want to get result after list then simply use q.ToList()
deviceCategories = new ObservableCollection<DeviceCategoryViewModel>(q.ToList());

Dynamic class based on string parameter

I have this:
public class Blah
{
public int id { get; set; }
public string blahh { get; set; }
}
public class Doh
{
public int id { get; set; }
public string dohh { get; set; }
public string mahh { get; set; }
}
public List<???prpClass???> Whatever(string prpClass)
where string prpClass can be "Blah" or "Doh".
I would like the List type to be class Blah or Doh based on what the string prpClass holds.
How can I achieve this?
EDIT:
public List<prpClass??> Whatever(string prpClass)
{
using (var ctx = new ApplicationDbContext())
{
if (prpClass == "Blah")
{
string queryBlah = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Blah>(queryBlah).ToList();
return result;
}
if (prpClass == "Doh")
{
string queryDoh = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Doh>(queryDoh).ToList();
return result;
}
return null
}
}
you have to have a common supertype:
public interface IHaveAnId
{
int id { get;set; }
}
public class Blah : IHaveAnId
{
public int id { get; set; }
public string blahh { get; set; }
}
public class Doh : IHaveAnId
{
public int id {get;set;}
public string dohh { get; set; }
public string mahh { get; set; }
}
then you can do:
public List<IHaveAnId> TheList = new List<IHaveAnId>();
and in some method:
TheList.Add(new Blah{id=1,blahh = "someValue"});
TheList.Add(new Doh{id =2, dohh = "someValue", mahh = "someotherValue"});
to iterate through the list:
foreach(IHaveAnId item in TheList)
{
Console.WriteLine("TheList contains an item with id {0}", item.id);
//item.id is allowed since you access the property of the class over the interface
}
or to iterate through all Blahs:
foreach(Blah item in TheList.OfType<Blah>())
{
Console.WriteLine("TheList contains a Blah with id {0} and blahh ='{1}'", item.id, item.blahh);
}
Edit:
the 2 methods and a int field holding the autovalue:
private int autoValue = 0;
public void AddBlah(string blahh)
{
TheList.Add(new Blah{id = autovalue++, blahh = blahh});
}
public void AddDoh(string dohh, string mahh)
{
TheList.Add(new Doh{id = autovalue++, dohh = dohh, mahh = mahh});
}
Another Edit
public List<object> Whatever(string prpClass)
{
using (var ctx = new ApplicationDbContext())
{
if (prpClass == "Blah")
{
string queryBlah = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Blah>(queryBlah).ToList();
return result.Cast<object>().ToList();
}
if (prpClass == "Doh")
{
string queryDoh = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Doh>(queryDoh).ToList();
return result.Cast<object>.ToList();
}
return null;
}
}
in the view you then have to decide what type it is. In asp.net MVC you can use a display template and use reflection to get a good design. But then i still don't know what technology you are using.
Yet another Edit
TestClass:
public class SomeClass
{
public string Property { get; set; }
}
Repository:
public static class Repository
{
public static List<object> Whatever(string prpClass)
{
switch (prpClass)
{
case "SomeClass":
return new List<SomeClass>()
{
new SomeClass{Property = "somestring"},
new SomeClass{Property = "someOtherString"}
}.Cast<object>().ToList();
default:
return null;
}
}
}
And a controller action in mvc:
public JsonResult Test(string className)
{
return Json(Repository.Whatever("SomeClass"),JsonRequestBehavior.AllowGet);
}
then i called it with: http://localhost:56619/Home/Test?className=SomeClass
And got the result:
[{"Property":"somestring"},{"Property":"someOtherString"}]
Is this what you are trying to do?
public class Blah
{
public int id { get; set; }
public string blahh { get; set; }
}
public class Doh
{
public int id { get; set; }
public string dohh { get; set; }
public string mahh { get; set; }
}
class Program
{
public static List<T> Whatever<T>(int count) where T: new()
{
return Enumerable.Range(0, count).Select((i) => new T()).ToList();
}
static void Main(string[] args)
{
var list=Whatever<Doh>(100);
// list containts 100 of "Doh"
}
}

Convert System.Linq.IQueryable to System.Collections.Generic.ICollection

I'm new to asp.net mvc & I'm trying to make a website with asp.net mvc 4 & EF6 where user can sort a table after login. I'm getting a compile error saying Cannot implicitly convert type System.Linq.IQueryable to System.Collections.Generic.ICollection. My codes are below,
Controller
public ActionResult Login(string sortOrder)
{
if (Session["UserNAME"] != null)
{
ViewBag.CodeSort = String.IsNullOrEmpty(sortOrder) ? "code_desc" : "";
var sortedOut = new MkistatVsUserLogin { mkistats = dsedb.mkistats.AsQueryable() }; //Error in this line
switch (sortOrder)
{
case "code_desc":
sortedOut = sortedOut.OrderByDescending(s => s.MKISTAT_CODE);
break;
default:
sortedOut = sortedOut.OrderBy(s => s.MKISTAT_CODE);
break;
}
return View(sortedOut.ToList());
}
else
{
return RedirectToAction("Home");
}
}
Model
public class MkistatVsUserLogin
{
public mkistat mkistats { get; set; }
public idx Idxs { get; set; }
}
How can I solve this problem. Need this help badly. Tnx.
UPDATES
Mkistat Model
public partial class mkistat
{
public string MKISTAT_CODE { get; set; }
public int MKISTAT_NUMBER { get; set; }
public string MKISTAT_QUOTE_BASES { get; set; }
public decimal MKISTAT_OPEN_PRICE { get; set; }
public decimal MKISTAT_HIGH_PRICE { get; set; }
public decimal MKISTAT_LOW_PRICE { get; set; }
public decimal MKISTAT_SPOT_TOTAL_VALUE { get; set; }
public string MKISTAT_LM_DATE_TIME { get; set; }
}
You need to modify your model and set mkistats type to IQueryable<mkistat> , you are passing IQueryable<mkistat> where your property is of Type mkistat not IQueryable<mkistat>, you have to do like this:
public class MkistatVsUserLogin
{
public IQueryable<mkistat> mkistats { get; set; }
public idx Idxs { get; set; }
}
and now in your action:
var sortedOut = new MkistatVsUserLogin
{
mkistats = dsedb.mkistats.AsQueryable();
};
If you want to do with List<mkistat> htne your model should be like:
public class MkistatVsUserLogin
{
public List<mkistat> mkistats { get; set; }
public idx Idxs { get; set; }
}
and in action:
var sortedOut = new MkistatVsUserLogin
{
mkistats = dsedb.mkistats.ToList();
};
Apparently mkistats are implementing I generic collection, and by calling .AsQueryable() you are making an iQueryable collection. If you need it to be queryable, change mkistats to implement iQueryable.
Otherwise you can call .toList() on it, but you lose queryable support and lazy loading, if those are important.
Edit: It looks like you don't need queryable, so just do this:
var sortedOut = new MkistatVsUserLogin { mkistats = dsedb.mkistats.AsQueryable().toList() };
Likely you could also just leave both the .AsQueryable() and the .toList(). But it doesn't really matter and I can't decipher how your code fits together.

Categories

Resources