Relate to : Set Properties that not null using linq and reflection
Hi experts
I change the code in above link:
public static void MyCopy<T>(this T src, T dest)
{
var notNullProps = typeof(T).GetProperties()
.Where(x => x.GetValue(src, null) != null);
foreach (var p in notNullProps)
{
p.SetValue(dest, p.GetValue(src, null),null);
}
}
and I wrote this code to copy peroperties:
NorthwindModel1.Order ord1 = new NorthwindModel1.Order() {CustomerID="Nima",Freight=1.33m,ShipCity="Agha" };
NorthwindModel1.Order ord2 = new NorthwindModel1.Order() ;
ord1.MyCopy(ord2);
but I got this error:
The EntityReference has already been initialized. InitializeRelatedReference should only be used to initialize a new EntityReference during deserialization of an entity object.
please help me to solve this problem
As mentioned in the comments, your reflective code is not the problem, but the fact that (as the exception message explicitly tells) you are indirectly triggering a reset of one of your entity references. My advice is twofold: either modify your reflective code to ONLY copy scalar properties (strings, dates, etc..) -- alternatively ignore references and collections -- OR use serialization:
public static T CloneBySerialization<T>(this T source) where T : EntityObject {
var serializer = new DataContractSerializer(typeof(T));
using (var ios = new MemoryStream()) {
serializer.WriteObject(ios, source);
ios.Seek(0, SeekOrigin.Begin);
return ((T) serializer.ReadObject(ios));
}
}
I must warn you that with this approach you will end up the full object graph or references. If the cloned object is an entity, YOU WILL NOT BE ABLE to use it/attach it to another context, due to the fact that references and foreign keys have been also copied, "verbatim", and this will all likely result in conflicts. The problem gets worse if you're using identity columns in keys.
I've done a lot of magic in my previous work on these matters, and as far as cloning is concerned, the code above is all you need. All, really.
However, to fix the context issue and the usability of your cloned entity, you will have to clear-off the references and under the assumptions that you're also working with "root" entities in the ¹ ↔ * directional relational graph (I hope I am remotely clear, because the story is long) the following will also be necessary.
public static void ClearReferences(this EntityObject entity) {
if (entity == null)
return;
foreach (var p in entity.GetType().GetProperties()) {
if (p.PropertyType.IsGenericType) {
var propertyType = p.PropertyType.GetGenericTypeDefinition();
if (propertyType == typeof(EntityReference<>)) {
var reference = p.GetValue(entity) as dynamic;
if (reference.EntityKey != null) {
reference.EntityKey = null;
((EntityObject) reference.Value).ClearReferences();
}
}
if (propertyType == typeof(EntityCollection<>)) {
var children = (p.GetValue(entity) as IEnumerable<EntityObject>).ToList(); // covariance
foreach (var child in children)
child.ClearReferences();
}
}
}
}
So the idea is that you first clone (via serialization/deserialization), then you "purify".
Related
I have a complex object / entity pulled from the database which has 50-60 fields. I've generated a form for this by hand and it was a pain in the bum. I don't want to have to now write c# code to manually apply each property.
I thought I could do this
using (DataContext db = new DataContext())
{
var _contact = db.Contact.Where(x => x.ID == model.Contact.ID).FirstOrDefault();
_contact = model.Contact;
db.SaveChanges();
}
However, this does not update the object. I guess the reference to the original is lost when I set _contact = model.Contact
I've found a lot of similar threads on SO with this solution (or variations):
var notNullProps = typeof(Contact).GetProperties()
.Where(x => x.GetValue(model.Contact, null) != null);
foreach (var p in notNullProps)
{
p.SetValue(_contact, p.GetValue(model.ContactWithMetaData.Contact, null), null);
}
However this always throws the error Non-static method requires a target
I've even tried using AutoMapper, even though my objects are of the same class. It seemed to lose the reference to the object too. It's late and I may be doing something stupid. Can anyone help?
UPDATE:
My AutoMapper Code:
Mapper.CreateMap<Contact, Contact>();
Mapper.Map(model.ContactWithMetaData.Contact, _contact);
db.SaveChanges();
UPDATE 2:
Sorry folks. It turns out I was doing something stupid. I was populating my form with model.Contact but had omitted a hidden field for Contact.ID. As a result Contact.ID and _contact were both null to begin with. Now that I've seen my mistake, I've gone back and tested. Both methods above work, and wahwahwah's method also works.
Sorry for wasting everyone's time!
I think this might help:
public void SetProperties(object source, object target)
{
var contactType = target.GetType();
foreach (var prop in source.GetType().GetProperties())
{
var propGetter = prop.GetGetMethod();
var propSetter = contactType.GetProperty(prop.Name).GetSetMethod();
var valueToSet = propGetter.Invoke(source, null);
propSetter.Invoke(target, new[] { valueToSet });
}
}
Only works if the properties of both objects are exactly the same (case sensitive.) Not as robust as AutoMapper...
By doing _contact = model.Contact; you are replacing the entire reference of _contact with the reference to model.Contact. The original object referred to by _contact is unchanged and hence there is nothing to save.
I'm using Entity Framework 6, Code First approach. I'll try to present my problem with a simple piece of code:
public void ViewEntity(MyEntity Entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
Db.MyEntities.Attach(Entity); // Exception
}
}
The exception message is: Attaching an entity of type 'MyProgram.MyEntity' failed because another entity of the same type already has the same primary key value.
From what I've read on MSDN it's an expected behaviour. But what I want on that last line is to first check if there is an entity with the same key already attached to a context; if it is, use it instead, and only otherwise attach my entity to context.
But I've failed to find a way to do so. There are many utility methods on ObjectContext instance (for example GetObjectByKey). I can't test them all 'cause they all ultimately need a qualifiedEntitySetName, and I don't have any in my real imlpementation, because this method should be on an abstract class and it should work for all entity types. Calling Db.Entity(this) is no use, there is no EntityKey which would have EntitySetName.
So all of this became complex really fast. And in my terms I just want to check if the object is already in "cache" (context), use it, otherwise use my object and attach it to this context.
To be clear, I have a detached object from a TreeNode.Tag in the first place, and I just want to use it again, or if it's impossible; if there already is one in the context), use that one instead. Maybe I'm missing some crucial concepts of EF6, I'm just starting out with EF.
I've found a solution for me. As I guessed correctly ObjectContext.GetObjectByKey method does what I need, but first I needed to construct qualifiedEntitySetName, and I found a way to do so. A tad bit cumbersome (using reflection, iterating properties of MyDbContext), but does not compare to a headache of a problem I made out of all this. Just in case, here's the patch of code that is a solution for me:
public SdsAbstractObject GetAttachedToContext()
{
var ObjContext = (SdsDbContext.Current as IObjectContextAdapter).ObjectContext;
var ExistingItem = ObjContext.GetObjectByKey(GetEntityKey()) as SdsAbstractObject;
if (ExistingItem != null)
return ExistingItem;
else
{
DbSet.Attach(this);
return this;
}
}
public EntityKey GetEntityKey()
{
string DbSetName = "";
foreach (var Prop in typeof(SdsDbContext).GetProperties())
{
if (Prop.PropertyType.IsGenericType
&& Prop.PropertyType.GenericTypeArguments[0] == ObjectContext.GetObjectType(GetType()))
DbSetName = Prop.Name;
}
if (String.IsNullOrWhiteSpace(DbSetName))
return null;
else
return new EntityKey("SdsDbContext." + DbSetName, "Id", Id);
}
An Entity can be in one of five stages : Added, Unchanged, Modified, Deleted, Detached.
public void ViewEntity(MyEntity entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
// Set the Modified state of entity or you can write defensive code
// to check it before set the state.
if (Db.Entry(entity).State == EntityState.Modified) {
Db.Entry(entity).State = EntityState.Modified
}
// Attached it
Db.MyEntities.Attach(Entity);
Db.SaveChanges();
}
}
Since EF doesn't know which properties are different from those in the database, it will update them all.
I have a requirement to copy ONLY populated values from one object that are not already populated in another object of the same type.
For example we are passed an object, it is only partially instantiated with data, we read the database to get a fully instantiated version of the object – however this may not have changes by the application committed to the database yet – hence we need to move any values from the database version into the passed in version of the object – without overwriting any values that may already exist in the passed in object (as these are the most up to date values).
The code below suggested by Adam Robinson in another post (see below very useful – thanks!) is a good starting point. However I need to extend this – as I only want to copy over values that are NOT already populated on the target object (i.e. need to check the destProperty is not null). However as an added complication, there are internal complex types declared within the object passed in, this code copies the high level sub groups over without going into the individual properties of the sub groups (i.e. any variables declared with the Root cdt I can try and check for null, but all the fields in sub cdts are simply copied over without going through the individual fields).
Any help would be greatly appreciated.
public static void CopyPropertyValues(object source, object destination)
{
var destProperties = destination.GetType().GetProperties();
foreach (var sourceProperty in source.GetType().GetProperties())
{
foreach (var destProperty in destProperties)
{
if (destProperty.Name == sourceProperty.Name &&
destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
destProperty.SetValue(destination, sourceProperty.GetValue(
source, new object[] { }), new object[] { });
break;
}
}
}
}
First you need to determine what populated means in your book.
Once you determine that, write you own version of the following IsDefaultValue method.
What I have written will answer with true in the following manner:
if it's a bool then it needs to have the false value
if it's an int then it needs to be 0
if it's any class then needs to be null
etc
So here's my version of the method:
public static bool IsDefaultValue(object #object) {
if (null == #object)
return true;
else if (#object.GetType().IsValueType) {
var isDefault = Activator.CreateInstance(#object.GetType()).Equals(#object);
return isDefault;
} else
return false;
}
Then, presuming that you're interested only in the non indexer properties, that you have no hierarchies and that you will always call this method with objects of the same type,
you could just filter out those properties which are not default in the source but are default in the destination.
Then you could traverse the instance graph recursively for those cases when you have a non-default value in your destination properties.
Please not that what I have written here is just a demonstration of how you might be able to accomplish your task. There are intricate details in your particular scenario which you need to address yourself as they are not obvious from you question.
For instance, I assumed it would be a good idea to add a stop condition for the recursive traversal (the remainingDepth parameter)
public static void CopyPropertyValues(object source, object destination, int remainingDepth = 3) {
// we've reached the farthest point we're allowed to go to
// anything beyond this point won't be affected by this method
if (remainingDepth == 0)
return;
// just a check to make sure the following lines won't backfire
if ((null == source) || (null == destination))
throw new ArgumentNullException();
// we'll need to also check that the 2 objects are of the same type
var type = source.GetType();
if (destination.GetType() != type)
throw new ArgumentException("The two objects should be of the same type");
var properties = type.GetProperties()
// just filter out the properties which are indexers (if any)
// and also those properties which are read or write only
.Where(property => (property.GetIndexParameters().Length == 0) &&
property.CanRead && property.CanWrite);
foreach (var property in properties) {
var sourceValue = property.GetValue(source, null);
var destValue = property.GetValue(destination, null);
if (!IsDefaultValue(sourceValue))
if (IsDefaultValue(destValue))
property.SetValue(destination, sourceValue, null);
else
if (sourceValue.GetType() == destValue.GetType())
CopyPropertyValues(sourceValue, destValue, remainingDepth - 1);
}
}
Please note that property enumeration is needed just once since the objects (as you said it yourself in the comments section) are of the same type.
Also please beware of Reflection when performance is of importance.
I was looking for a general solution for cascading delete in linq to sql from c#, but I could not find any yet.
So I came up with my own solution, which handles one to many relations.
It would not be the general way to delete entities, but some edge case requires it. So before it goes to the production environment, I would like to know that what are you think about it?
It seems to work, but what are the pros and cons, where it can fail? Note that it only supposed to traverse the relation tree downside.
public class CascadingDeleteHelper
{
public void Delete(object entity, Func<object, bool> beforeDeleteCallback)
{
if (!(entity is BusinessEntity))
{
throw new ArgumentException("Argument is not a valid BusinessEntity");
}
if (beforeDeleteCallback == null || beforeDeleteCallback(entity))
{
Type currentType = entity.GetType();
foreach (var property in currentType.GetProperties())
{
var attribute = property
.GetCustomAttributes(true)
.Where(a => a is AssociationAttribute)
.FirstOrDefault();
if (attribute != null)
{
AssociationAttribute assoc = attribute as AssociationAttribute;
if (!assoc.IsForeignKey)
{
var propertyValue = property.GetValue(entity, null);
if (propertyValue != null && propertyValue is IEnumerable)
{
IEnumerable relations = propertyValue as IEnumerable;
List<object> relatedEntities = new List<object>();
foreach (var relation in relations)
{
relatedEntities.Add(relation);
}
relatedEntities.ForEach(e => Delete(e, beforeDeleteCallback));
}
}
}
}
SingletonDataContext.DataContext.GetTable(currentType).DeleteOnSubmit(entity);
SingletonDataContext.DataContext.SubmitChanges();
}
}
}
Thank you very much,
negra
Pros of your solution:
It's generic
It does not require database changes on schema updates
Cons:
Error prone (more custom code)
Performance is (most likely) not optimal
There is a less complex, but more maintenance heavy solution, which is adding ON CASCADE DELETE rules into your database.
considering that fairly static data should not be re-evaluated but cached instead, I wondered if it is possible to use Reflection to obtain class properties once, and then cache them so that I could dynamically evaluate object properties and read/assign values, but not have the Reflection overhead every time I do that. Is this possible (Sample code?) ?
To clarify a bit, lets say I have this class:
public class Cloud
{
Boolean IsWhite;
}
and I'm trying to now make a method that allows me to do something like this (pseudocode):
Update(myCloudInstance, new {IsWhite, true});
Update should now check with the cache first if it knows already the properties of Cloud (typeof(myCloudInstance)), and then use cached information to assign the property "IsWhite" the value "true" instead of doing Reflection again.
Any ideas on how to do this?
It's not clear exactly what you're doing, but caching can certainly make a difference with reflection.
In particular, if you're invoking methods (or property getters/setters) and can do so in a type-safe way as far as the calling code is concerned, it can make a huge difference if you convert the MethodInfo into a strongly-typed delegate once and then reuse that.
If you could give us a complete example of what you're trying to do, that would help us to come up with more specific ideas or even code. If you're just going to cache a PropertyInfo that may not have as much (or any) effect - it's possible that the normal Type.GetProperty (etc) methods are already pretty fast. As ever with performance questions, the key is to measure what you're actually doing. Make a change and measure again, etc.
The cost of reflection does not need to be as big as you think. In addition to delegates (that Jon discusses) you can also use things like HyperDescriptor to minimise the cost of reflection without changing the code much - it simply becomes PropertyDescriptor instead:
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(myCloudInstance);
// ideally cache props, but not essential
then
object val = props["IsWhite"].GetValue(myCloudInstance);
or if you use it lots, consider storing the PropertyDescriptor somewhere, too.
However... like Jon, I'm really not 100% sure what you're trying to do!
I created a hashtable to cache the reflection results. First time, it's neccessary to make a call to GetProperties and store the results into the hastable. Next times, first check the hashtable for the List of PropertyInfo objects. If exists, use it. If not, invoke GetProperties.
I use this to map a datareader to a List of Entities.
My implementation is based on: A Defense on Reflection in .Net, by Nick Harrison (http://www.simple-talk.com/dotnet/.net-framework/a-defense-of-reflection-in-.net/).
So, there it is:
public class MapeadorDataReaderListaObjetos
{
private Hashtable properties;
private Hashtable Properties
{
get
{
if (properties == null)
properties = new Hashtable();
return properties;
}
set { properties = value; }
}
private void LoadProperties(object targetObject, Type targetType)
{
var flags = BindingFlags.DeclaredOnly| BindingFlags.Instance| BindingFlags.Public;
if (properties == null)
{
List<PropertyInfo> propertyList = new List<PropertyInfo>();
PropertyInfo[] objectProperties = targetType.GetProperties(flags);
foreach (PropertyInfo currentProperty in objectProperties)
{
propertyList.Add(currentProperty);
}
properties = new Hashtable();
properties[targetType.FullName] = propertyList;
}
if (properties[targetType.FullName] == null)
{
List<PropertyInfo> propertyList = new List<PropertyInfo>();
PropertyInfo[] objectProperties = targetType.GetProperties(flags);
foreach (PropertyInfo currentProperty in objectProperties)
{
propertyList.Add(currentProperty);
}
properties[targetType.FullName] = propertyList;
}
}
public void MapearDataReaderListaObjetos <T> (IDataReader dr, List<T> lista) where T: new()
{
Type businessEntityType = typeof(T);
List<T> entitys = new List<T>();
T miObjeto = new T();
LoadProperties(miObjeto, businessEntityType);
List<PropertyInfo> sourcePoperties = Properties[businessEntityType.FullName] as List<PropertyInfo>;
while (dr.Read())
{
T newObject = new T();
for (int index = 0; index < dr.FieldCount; index++)
{
for (int _indice = 0; _indice < sourcePoperties.Count; _indice++)
{
if (sourcePoperties[_indice].Name.ToUpper() == dr.GetName(index).ToUpper());
{
string _tipoProp = sourcePoperties[_indice].PropertyType.ToString();
PropertyInfo info = sourcePoperties[_indice] as PropertyInfo;
if ((info != null) && info.CanWrite)
{
info.SetValue(newObject, dr.GetValue(index), null);
}
}
}
}
entitys.Add(newObject);
}
dr.Close();
lista = entitys;
}
}
Then, I call it from my DataAcces Layer, like this:
public List <Entities.ENFactura> ListaxIdFactura (SqlTransaction Tr, Entities.ENFactura oBEFactura)
{
SqlConnection Cn = new SqlConnection();
Cn = _Connection.ConexionSEG();
List<Entities.ENFactura> loBEFactura = new List<Entities.ENFactura>();
using (Cn)
{
Cn.Open();
SqlDataReader drd = (odaSQL.fSelDrd(Cn, Tr, "Pa_CC_Factura_Listar_x_IdProveedor", oBEFactura));
if (drd != null)
{
if (drd.HasRows)
{
mapeador.MapearDataReaderListaObjetos <ENFactura>(drd, loBEFactura);
}
}
}
return (loBEFactura);
}
So, this way, the DAL gets a datareader, map it to a list of business entities, and return it to the Business Logic Layer.
This class (MapeadorDataReaderListaObjetos) has some issues still, particularly at:
info.SetValue(newObject, _valor, null);
newObject and _valor must be the same type or you'll get an exception (conversion from System.Int64 to System.Int32, in case your entity property is Int32 and its corresponding field at the database table is bigint, for example).
Also, if an entity property is another entity, this will not work, because datareaders do not return entity objects.
Obviously, this can be improved.
Regarding reflection and delegates, i found this article: Reflection - Slow or Fast? Demonstration with Solutions, by Abhishek Sur, at
http://www.abhisheksur.com/2010/11/reflection-slow-or-faster-demonstration.html
Another good article is: Dodge Common Performance Pitfalls to Craft Speedy Applications, by Joel Pobar, at http://msdn.microsoft.com/en-us/magazine/cc163759.aspx.
Hope this helps.
I think the best way to do it is to get the getter or setter method, convert it to a delegate, and work with the delegate, there is no faster way:
PropertyInfo propertyInfoProperty1 = type.GetType().GetProperty("Property1");
Func<TYPE, string> get_Property1 = (Func<TYPE, string>)Delegate.CreateDelegate(typeof(Func<TYPE, string>), propertyInfoProperty1.GetGetMethod());
Then call the getter method:
string value = get_Property1(type);
You can cache the delegates.
Dynamic assembly should help with the concern about reflection performance. Somebody has implemented property assessors using dynamic assembly here.