I have a web form with around 50 fields that is used for crud operations on an Oracle DB, I am using EF6.
Currently, I accomplish this like so:
private GENERIC_FTP_SEND GetFields()
{
GENERIC_FTP_SEND ftpPartner = new GENERIC_FTP_SEND();
//Contact Info
ftpPartner.FTP_LOOKUP_ID = FTP_LOOKUP_IDTB.Text;
ftpPartner.PARTNER_NAME = PARTNER_NAMETB.Text;
ftpPartner.REMEDY_QUEUE = REMEDY_QUEUETB.Text;
ftpPartner.PRIORITY = PRIORITYBtns.SelectedValue;
ftpPartner.CONTACT_EMAIL = CONTACT_EMAILTB.Text;
ftpPartner.CONTACT_NAME = CONTACT_NAMETB.Text;
ftpPartner.CONTACT_PHONE = CONTACT_PHONETB.Text;
...
}
where GENERIC_FTP_SEND is the name of the virtual DbSet in my Model.context.cs.
This works fine but is not reusable in the least. What I would like to accomplish is to have some code that allows me to iterate through the attributes of ftpPartner and compare them to the field id for a match. Something like this:
var n =0;
foreach (Control cntrl in ControlList){
if(cntrl.ID == ftpPartner[n]){
ftpPartner[n] = cntrl.Text;
}
n++;
}
In case you need/want to see it here is my Model.context.cs
public partial class Entities : DbContext{
public Entities(): base("name=Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<GENERIC_FTP_SEND> GENERIC_FTP_SEND { get; set; }
}
I saw the question here but I am not sure how to implement that in my case.
Entity Framework 6: is there a way to iterate through a table without holding each row in memory
You can achieve that with reflection:
var type = typeof(GENERIC_FTP_SEND);
foreach (Control cntrl in ControlList){
Object value = null;
if (cntrl is TextBox){
value = (cntrl as TextBox).Text;
} else if (cntrl is GroupBox){
value = (cntrl as GroupBox).SelectedValue;
} //etc ...
PropertyInfo pInfo = type.GetProperty(cntrl.ID);
if (pInfo != null && value != null){
pInfo.SetValue(ftpPartner, value, null);
}
}
You can also use the Entity Framework context object as well to accomplish the same if you know you are going to insert.
var x = new GENERIC_FTP_SEND();
// Add it to your context immediately
ctx.GENERIC_FTP_SEND.Add(x);
// Then something along these lines
foreach (Control cntrl in ControlList)
{
ctx.Entry(x).Property(cntrl.Name).CurrentValue = ctrl.Text;
}
Related
I have a class with 10 properties and I am using reflection to assign values to these properties. My Property names are like,
private double? _col1;
public double? Col1
{
get
{
return _col1;
}
set
{
_col1 = value;
OnPropertyChanged("Col1");
}
}
private double? _col2;
public double? Col2
{
get
{
return _col2;
}
set
{
_col2 = value;
OnPropertyChanged("Col2");
}
}
And I am using them like,
MyClass md = new MyClass();
PropertyInfo[] properties = typeof(MyClass).GetProperties();
{
foreach (PropertyInfo property in properties)
{
double? d1 = from some method();
if (property.PropertyType == typeof(Double?))
{
if (property.GetValue(md) == null)
{
property.SetValue(md, d1);
}
}
}
}
Here I want to use an orderby property name of the class. How ??
PropertyInfo[] properties = typeof(MyClass).GetProperties();
You need to sort properties alphabetically based on their name.
Add the following line before the foreach() loop
Array.Sort(properties, (x,y)=> x.Name.CompareTo(y.Name)
I think this is an XY problem.
I think what you actually need could be something like this:
public class MyClass
{
public ObservableCollection<double?> Cols { get; }
public MyClass(int initialSize)
{
if(initialSize < 0)
{
throw new ArgumentOutOfRangeException(nameof(initialSize));
}
Cols = new(Enumerable.Repeat((double?)null, initialSize));
}
}
Then, ditching the reflection, you can just use a for loop on the observable collection. The observable collection is great cause it already raises events informing about changes.
MyClass md = new MyClass(2);
for (int i = 0; i < md.Cols.Count; i++)
{
double? d1 = from some method();
if (md.Cols[i] is null)
{
md.Cols[i] = d1;
}
}
Then, if someone needs to listen to changes made to Cols, they subscribe to the Cols.CollectionChanged event.
Order of properties in reflection is the same as in their declaration in program code. Property declared first is first. Property declared second is second. I have never tested myself, but in case of partial classes, when a class is split into several files, the situation might be tricky.
how we can ensure that it takes first Col1, then secondly Col2, etc ??
To ensure, You should use names of the properties. Example
if (property.Name == nameof(MyClass.Col1))
{
property.SetValue(instanceOfClassName, value);
}
Alternative for if is switch statement
switch (property.Name)
{
case nameof(MyClass.Col1):
break;
case nameof(MyClass.Col2):
break;
}
If you want to order the properties by name then simply use OrderBy LINQ query
var orderedByNameProperties = properties.OrderBy(property => property.Name);
I am a little bit confused about how to read data from Excel. I am trying to import Excel for updating product list, I create an Excel model; I added all basic properties like name, price, quantity, etc. into this model. I will read all Excel and map into this model. That's ok, then I will give this model to EF Core 5 to save to SQL Server.
public class ExcelModel
{
public string Name { get; set }
public int Price { get; set }
public int Quantity { get; set }
}
I have a problem with product options. According to my DB schema, I have one table of products, one for options, one for option values, one for productOptionRelation.
Can you suggest another solution way or just solve on my way?
My colleges did this created field corresponding to values. like option1 and optionValue1, option2 and optionValue2 many of them, because each product could have many options. Model look like that, 20 option and 20 value was declared here and they manually map all these
For a temporary solution, I limited this option up to 5 and I created an list. and encapsulate all of them into list
public class ExcelOptionViewModel
{
public string Option { get; set; }
public string Value { get; set; }
}
This is my temp model, I encapsulated like that.
public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
public string Option1
{
get { return OptionModels[0].Option; }
set
{
this.OptionModels.Insert(0, new ExcelOptionViewModel { Option = value });
}
}
public string Option1Value
{
get { return OptionModels[0].Value; }
set { this.OptionModels[0].Value = value; }
}
This would be unlimited, You should enter how much you want
I have 2 solutions still I am researching one is, creating a method inside the excelviewmodel, this method will add all options and values into a list or I will use reflection, I am looking something like underlying type I will all option and values this underlying base type or something, when property loop came here, checking the type and assign all option1,option2,option3 or name like that properties to List<string> options, and same for the option values. I will use reading like option[0] and optionvalue[0]
Excel column names must be different because I read excel and turn it into datatable. Datatable column names must be different, it's not valid for reading into datatable
I used basically excel to data table function I can't remember but probably I found it in StackOverflow. Also, I added a feature there If some cell is null it will miss.
public List<T> ConvertDataTableToList<T>(DataTable dt)
{
//datatable clomun names
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();
//selection properties equals to columnnames because I dont want loop for all props
var properties = typeof(T).GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower()));
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
try
{
if (row[pro.Name] != DBNull.Value)
pro.SetValue(objT, row[pro.Name], null);
else
pro.SetValue(objT, null, null);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
return objT;
}).ToList();
}
I am looking something here when option1 or option2 comes here it would put this into a list
Also in my dt to model converter I dont want to use If but if some data value is null It throws an error which cant convert from dbnull value. If you have a suggest for it I would like release if condition :)
When All done I will map this excelviewmodel to product model something like this
foreach (var prop in SideParams.columns)
{
var source = row.GetType().GetProperty(prop);
var destination = product.GetType().GetProperty(prop);
if (destination != null && source.GetValue(row) != null)
{
Type t = Nullable.GetUnderlyingType(destination.PropertyType) ?? destination.PropertyType;
object safeValue = Convert.ChangeType(source.GetValue(row), t);
destination.SetValue(product, safeValue);
}
}
I saw something here
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0
it about binding flangs when reflecting model. "Specifies flags that control binding and the way in which the search for members and types is conducted by reflection." If there is way I can redirect option(1-2-3-4-5-6...) to list options
thanks for the help I solved my problem. If you need something like that, my solution is;
As you know OptionModels is what I created before, AddOptipns function is a new one I use for add data to list,
The function work with the ref, otherwise it must be static, if I turn it static, option models also must be static, so I can't access the list.
public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
public void AddOptions(ref String option, ref String value)
{
OptionModels.Add(new ExcelOptionViewModel { Option = option.Trim(), Value = value.Trim() });
}
And also add some new parts to convert model function,
calling that AddOptions method with reflection, I got an example from here
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0
I was inspired by the swap example there.
public List<T> ConvertDataTableToList<T>(DataTable dt)
{
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();
//selection properties equals to columnnames because I dont want loop for all props
var type = typeof(T);
var properties = type.GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower())).ToList();
var productOptions = columnNames.Where(x => x.Contains("option")).ToList() ?? new List<string>();
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
try
{
if (row[pro.Name] != DBNull.Value)
pro.SetValue(objT, row[pro.Name], null);
else
pro.SetValue(objT, null, null);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
for (var i = 0; i < productOptions.Count(); i += 2)
{
object[] argValues = new object[] { row[productOptions[i]].ToString(), row[productOptions[i + 1]].ToString() };
String[] argNames = new String[] { "option", "value" } ;
var method = type.GetMethod("AddOptions");
method.Invoke(objT, argValues);
}
return objT;
}).ToList();
}
here is the added data :)
I'm trying to put a series of functions together for Entity Framework use. The idea is that I want to pass generic classes all into one routine, and then have the routine "type" it for me and act accordingly. I can't seem to figure out how to marry the Class back up with it's PropertyType.
public void AddUpdate(string classType, Object o)
{
//This gets the Type of my Class Object ok.
Type mType = Type.GetType(GetType().Namespace + "." + classType, true);
var meContext = new ClsContext(_ConnectionString);
//This retrieves the correct primary key for my the Class Object.
string key = FncGetPrimaryKey(meContext, classType + "s");
//I've tried this as a PropertyInfo as well instead of a Var
var keyID = o.GetType().GetProperty(key);
//I've tried this as Var as well as Dynamic
dynamic obj = o;
//Now I'm stuck, because I want to evaluate the property,
//but I get an error "<MyClass> does not contain a reference for 'keyID'
if (obj.keyID == 0) //ERROR ON THIS LINE
{
meContext.Entry(o).State = EntityState.Added;
}
else
{
meContext.Entry(o).State = EntityState.Modified;
}
meContext.SaveChanges();
}
Although, I did not fully understand why do you want it like this, I think, I understood what you are trying to achieve. This is how I do it in case if I want to add some common object:
public void AddUpdate<T>(T obj) where T : IEntity
{
using(var ctx = new ClsContext(_ConnectionString))
{
if (obj.keyID == 0)
{
ctx.Entry(o).State = EntityState.Added;
}
else
{
ctx.Entry(o).State = EntityState.Modified;
}
ctx.SaveChanges();
}
}
public interface IEntity
{
int keyId {get;set;}
}
Try:
var keyValue = keyID.GetValue(o);
if (keyValue.Equals(0)) {...
I have an entity which is not connected to my dbcontext. I want to change that. However there is already another instance of the same entity attached to dbcontext. If I just add my new entity, I get an error, that an entity with the same primary key is already attached.
I tried multiple different variants of removing the old entity from dbcontext without any success. How can I replace the old instance with the new one?
Note: I don't want to copy the values, I want to attach this very instance of my new entity to dbcontext.
var entity = new MyEntity { Id = 1 };
var logicalDuplicate = dbcontext.Set<MyEntity >().Local
.FirstOrDefault(e => e.Equals(entity));
if (logicalDuplicate != null)
{
// remove logicalDuplicate from dbcontext
}
dbcontext.MyEntity.Attach(entity);
For clarification: I have overridden Equals to check for Id instead of reference.
Try this:
if (logicalDuplicate != null)
{
dbcontext.Entry(logicalDuplicate).State = EntityState.Detached;
dbcontext.MyEntity.Attach(entity);
dbcontext.Entry(entity).State = EntityState.Modified;
}
else
{
dbcontext.MyEntity.Add(entity);
}
How to get related entries
I investigated that and want to share with my results.
I used reflection as short way to get entity properties names. But it's possible to get it without reflection as mentioned #Florian Haider. You can use
answer and this.
// Found loaded related entries that can be detached later.
private HashSet<DbEntityEntry> relatedEntries;
private DbContext context;
private List<string> GetPropertiesNames(object classObject)
{
// TODO Use cache for that.
// From question https://stackoverflow.com/questions/5851274/how-to-get-all-names-of-properties-in-an-entity
var properties = classObject.GetType().GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
return properties.Select(t => t.Name).ToList();
}
private void GetRelatedEntriesStart(DbEntityEntry startEntry)
{
relatedEntries = new HashSet<DbEntityEntry>();
// To not process start entry twice.
relatedEntries.Add(startEntry);
GetRelatedEntries(startEntry);
}
private void GetRelatedEntries(DbEntityEntry entry)
{
IEnumerable<string> propertyNames = GetPropertiesNames(entry.Entity);
foreach (string propertyName in propertyNames)
{
DbMemberEntry dbMemberEntry = entry.Member(propertyName);
DbReferenceEntry dbReferenceEntry = dbMemberEntry as DbReferenceEntry;
if (dbReferenceEntry != null)
{
if (!dbReferenceEntry.IsLoaded)
{
continue;
}
DbEntityEntry refEntry = context.Entry(dbReferenceEntry.CurrentValue);
CheckReferenceEntry(refEntry);
}
else
{
DbCollectionEntry dbCollectionEntry = dbMemberEntry as DbCollectionEntry;
if (dbCollectionEntry != null && dbCollectionEntry.IsLoaded)
{
foreach (object entity in (ICollection)dbCollectionEntry.CurrentValue)
{
DbEntityEntry refEntry = context.Entry(entity);
CheckReferenceEntry(refEntry);
}
}
}
}
}
private void CheckReferenceEntry(DbEntityEntry refEntry)
{
// Add refEntry.State check here for your need.
if (!relatedEntries.Contains(refEntry))
{
relatedEntries.Add(refEntry);
GetRelatedEntries(refEntry);
}
}
Edit This finds the original product, removes it, and adds the new one:
static void UpdateDatabase()
{
Context context = new Context();
Product product1 = context.Products.Find(1);
context.Products.Remove(product1);
Product product2 = new Product(){ProductId = 1, Name = "Product2"};
context.Products.Add(product2);
context.SaveChanges();
}
Best way to salve this problem is
db is my database Object
updateprice is my database entity object
ep is my old same database entity object
db.Entry(updateprice).CurrentValues.SetValues(ep);
I have this code that I am currently adapting to use nested entity (entity framework). I have an entity which contains 2 property which are 2 children entities.
The first step was to read the metadata on both classes, starting from the first classes, building up a list of properties. This is completed. Now I need to iterate over my object to find the good property to do the DataBinding.
This is what I currently have :
variables example :
datasource = namespace.A
propriete = {System.Nullable`1[System.Int32] count}
propriete.DeclaringType {Name = "prop2" FullName = "Namespace.Metadata.prop2"}
code :
if (this.datasource != null)
{
var y = (T)this.datasource;
var propList = typeof(T).GetProperties();
if (propList.ToList().Contains(propriete))
{
TextBox.Text = DataBinder.Eval(y, propriete.Name).ToString();
}
else
{
TextBox.Text = ":( need child-support!";
}
}
My main problem is that my object type is unknown till runtime (Type T) so I have no idea on how to find my field.
Quick model :
Class A {
public B prop1;
public C prop2;
}
Class B {
int count;
string name;
}
Class C {
int count;
string name;
}
A.prop1.count = 1;
A.prop1.name = "a";
A.prop2.count = 2;
A.prop2.name = "b";
Right now, my property name are all unique (more specific than count/name), but I expect them to be the same (count/name) at some point.
propriete will probably have to "filter" with DeclaringType/ReflectedType for non-unique name.
A brute-force solution considering unique name, although not elegant, might be accepted.
Extra problem : propriete use another partial class contains in the metadata namespace while datasource use the main class.
(... And if you are curious as to what this system does : It builds a html table (with .net controls) based on an entity based on this metadata.entity dataAttribute.)
You can get to the type information by calling GetType() on an instance. So in your case it would be:
var propList = this.datasource.GetType().GetProperties()
Here's what I ended up with :
if (this.datasource != null)
{
var y = (T)this.datasource;
var propList = typeof(T).GetProperties();
//try to assign the prop but it might be on a child-prop.
try
{
retour = DataBinder.Eval(y, propriete.Name).ToString();
}
catch
{
foreach (PropertyInfo prop in propList)
{
if ((prop.PropertyType).FullName.Contains("Env."))
{
var childPropList = prop.PropertyType.GetProperties();
foreach (PropertyInfo childProp in childPropList)
{
if (((System.Reflection.MemberInfo)(childProp)).Name == propriete.Name)
{
var x = DataBinder.GetPropertyValue(y, prop.Name);
retour = DataBinder.Eval(x, propriete.Name).ToString();
}
}
}
}
}
}
For now it works, but it might not be versatile enough in a near future.