I am currently trying to create a Class which employs generics in order to reduce the amount of work needed for future development. As I add Tables to the LINQ To SQL Designer, certain basic Methods are used in each one. Rather than reproduce them in every Partial Class associated with every new Table, I would like to employ a single generic Class. The issue is that any changes made to the Entities are not recognized and therefore not submitted.
Public Partial Class ABC
{
Public Static Bool Synchronize(string source, string destination)
{
try
{
DataContext destinationDB = DataConnection.Destination(destination);
Table<ABC> destinationABCs = destinationDB.ABCs;
DataContext sourceDB = DataConnection.Destination(source)
Table<ABC> sourceABCs = sourceDB.ABCs;
foreach (ABC ABCCode in sourceABCs)
{
ABC destABCCode = destinationABCs.SingleOrDefault(x => x.Id == ABCCode.Id);
bool same = EntityProcessing.AreIdentical(ABCCode, destABCCode);
if (same == false)
{
destABCCode = (ABC)EntityProcessing.Synchronize(ABCCode, destABCCode);
}
}
ChangeSet test = destinationDB.GetChangeSet(); // Test Line For Debugging
destinationDB.SubmitChanges();
}
return true;
}
}
The next Class is:
Public Static Class EntityProcessing
{
Public Static Bool AreIdentical(Object sourceEntity, Object destinationEntity)
{
if (sourceEntity.GetType() == destinationEntity.GetType())
{
Type t = sourceEntity.GetType();
FieldInfo[] tList = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo fi in tList)
{
if ((fi.GetValue(sourceEntity) != null ? fi.GetValue(sourceEntity).ToString()
: null) == (fi.GetValue(destinationEntity) != null ?
fi.GetValue(destinationEntity).ToString() : null))
{ continue; }
else
{ return false; }
}
return true;
}
else
{ return false; }
}
Public Static Object Synchronize(Object sourceEntity, Object destinationEntity)
{
if (sourceEntity.GetType() == destinationEntity.GetType())
{
Type t = sourceEntity.GetType();
FieldInfo[] tList = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo fi in tList)
{
fi.SetValue(destinationEntity, fi.GetValue(sourceEntity));
}
}
return destinationEntity;
}
}
I have tried modifying the EntityProcessing.Synchronize method into a Void method as well. Neither works. Both will return the correct Entity with the Fields set to the appropriate results. The issue lies in the fact that LINQ does not recognize the Entities as having changed.
If I add a temporary line of ChangeSet test = destinationDB.GetChangeSet();, the Updated count is zero. The loss appears to be in the conversion to Objects.
I have tried setting the Parameter Type to ABC on the EntityProcessing.Synchronize() method and modifying a Field, and the Updated count in test is correct. How do I resolve this?
How do I submit the updated entities to the database or rather, how do I get LINQ to recognize these entities are being changed and needing an update?
Do you mean: Public Static Bool Synchronize<ABC>(string source, string destination) with "ABC" as the generic type?
However, I don't think your .ABCs will work that simply. You may have to use reflection to get at the proeprty with that particular name. For example, first use reflection to get the name of the type parameter (ABC), and then use reflection to get the table field from the data source based on this type name.
Related
I mapped some access-tables to classes. I created properties and bound them to columns. I used the Storage-property like the following
string _ColValue;
[Column(Storage = "_ColValue")]
public string ColValue
{
get { return _ColValue; }
set { _ColValue = value; }
}
The columns contains string values but they have many spaces at the end. They come always with a constant length. Now I want to trim the values to remove the spaces. I thougt it would be nice to manipulate the string directly while mapping from the datatable to my properties.
My intesion was to iterate all private fields of mapped properties in a method i call in the constructor.
void TrimPropValues()
{
FieldInfo[] fi = this.GetType().GetFields(BindingFlags.NonPublic
| BindingFlags.Instance);
foreach (FieldInfo fiItem in fi)
{
if (fiItem.FieldType.Equals(typeof(string))
&& fiItem.GetValue(this) != null)
{
fiItem.SetValue(this, fiItem.GetValue(this).ToString().TrimEnd(' '));
}
}
}
When I call the method in the constructor, the fields are not filled. The mapping seems happen later. What is the right moment to call the method? When does the mapping happen or rather how can I reach the moment of mapping?
You can encapsulate this nicely in the context subclass itself:
public class MyContext : DbContext
{
public MyContext()
: base()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += (sender, e) =>
{
TrimStringValues(Entry(e.Entity));
};
}
void TrimStringValues(DbEntityEntry entry)
{
foreach (var property in entry.CurrentValues.PropertyNames)
{
var currentValue = entry.CurrentValues[property] as string;
if (currentValue != null)
entry.CurrentValues[property] = currentValue.Trim();
}
}
}
In the context's constructor the underlying ObjectContext's ObjectMaterialized event is subscribed to. In the event handler, each materialized entity is subjected to the TrimStringValues method. In this method, EF's own infrastructure is used to trim string values. This is faster than doing the same by reflection.
Say I have following Asset class:
class Asset
{
public int Id { get; set; }
public string Name { get; set; }
}
Now I want to write a method GetPropertyInfo(a=>a.Name); and this method gives me the PropertyInfo of Asset.Name. I should be able to call this method like:
EDIT Example Method Call
PropertyInfo propInfo = GetPropertyInfo(a=>a.Name);
I have a List<PropertyInfo> so I want to match a given lambda expression with those in my list.
if(Possible on Compact Framework 3.5 && using C#)
How?
else
Please Notify
Thanks.
This can be done under .NETCF 3.5.
private List<Asset> m_list;
private Asset[] GetPropertyInfo(string name) {
var items = m_list.Where(a => a.Name == name);
if (items != null) {
return items.ToArray();
} else {
return null;
}
}
You will, however, need to initialize the m_list and fill it with your data first.
UPDATE:
So, your list is of type PropertyInfo and you want a call to get the type that matches a particular Asset object.
If that is correct, you could simply edit the code above to be as follows:
private List<PropertyInfo> m_list;
private PropertyInfo GetPropertyInfo(Asset a) {
return m_list.FirstOrDefault(x => x.Name == a.Name);
}
I am not sure how you are getting the List<PropertyInfo>, though. I was able to pull a single PropertyInfo object using the code below:
private PropertyInfo GetPropertyInfo() {
var t = Type.GetType("System.Reflection.MemberInfo");
return t.GetProperty("Name");
}
There was nothing useful in this item.
This is my code where I create a "copy" of one object (Entity) into a custom object.
It copies just properties with the same name in both source and target.
My problem is when an Entity has a navgiaton to another Entity, for this case I added a custom attribute that I add above the property in the custom class.
For example the custom class looks like:
public class CourseModel:BaseDataItemModel
{
public int CourseNumber { get; set; }
public string Name { get; set; }
LecturerModel lecturer;
[PropertySubEntity]
public LecturerModel Lecturer
{
get { return lecturer; }
set { lecturer = value; }
}
public CourseModel()
{
lecturer = new LecturerModel();
}
}
The problem is in targetProp.CopyPropertiesFrom(sourceProp); line, when I try to call extension method again (to copy the nested object) ,because the type is determined on run time, extension method couldn't resolved on compile time.
Maybe I am missing something...
public static void CopyPropertiesFrom(this BaseDataItemModel targetObject, object source)
{
PropertyInfo[] allProporties = source.GetType().GetProperties();
PropertyInfo targetProperty;
foreach (PropertyInfo fromProp in allProporties)
{
targetProperty = targetObject.GetType().GetProperty(fromProp.Name);
if (targetProperty == null) continue;
if (!targetProperty.CanWrite) continue;
//check if property in target class marked with SkipProperty Attribute
if (targetProperty.GetCustomAttributes(typeof(SkipPropertyAttribute), true).Length != 0) continue;
if (targetProperty.GetCustomAttributes(typeof(PropertySubEntity), true).Length != 0)
{
//Type pType = targetProperty.PropertyType;
var targetProp = targetProperty.GetValue(targetObject, null);
var sourceProp = fromProp.GetValue(source, null);
targetProp.CopyPropertiesFrom(sourceProp); // <== PROBLEM HERE
//targetProperty.SetValue(targetObject, sourceEntity, null);
}
else
targetProperty.SetValue(targetObject, fromProp.GetValue(source, null), null);
}
}
You'll have to cast first.
((BaseDataItemModel)targetProp).CopyPropertiesFrom(sourceProp);
Either you need to cast targetProperty to BaseDataItemModel so you can call the extension method on it (edit: as in agent-j's answer), or otherwise you could just forget about that base class. Why does your reflection algorithm need it? It could work on any class, and is directed purely by the attributes on properties.
And if it works on any object, it shouldn't be an extension method.
What is the easiest way to take an objects and convert any of its values from null to string.empty ?
I was thinking about a routine that I can pass in any object, but I am not sure how to loop through all the values.
When your object exposes it's values via properties you can write something like:
string Value { get { return m_Value ?? string.Empty; } }
Another solution is to use reflection. This code will check properties of type string:
var myObject = new MyObject();
foreach( var propertyInfo in myObject.GetType().GetProperties() )
{
if(propertyInfo.PropertyType == typeof(string))
{
if( propertyInfo.GetValue( myObject, null ) == null )
{
propertyInfo.SetValue( myObject, string.Empty, null );
}
}
}
Using reflection, you could something similar to :
public static class Extensions
{
public static void Awesome<T>(this T myObject) where T : class
{
PropertyInfo[] properties = typeof(T).GetProperties();
foreach(var info in properties)
{
// if a string and null, set to String.Empty
if(info.PropertyType == typeof(string) &&
info.GetValue(myObject, null) == null)
{
info.SetValue(myObject, String.Empty, null);
}
}
}
}
Presumably, you have a report or a form somewhere showing "null" all over the place, instead of a nice, pleasant "".
It's best to leave the nulls as they are, and modify your display code wherever appropriate. Thus, a line like this:
label1.Text = someObject.ToString();
should become:
if (someObject == null)
{
label1.Text = ""; // or String.Empty, if you're one of *those* people
}
else
{
label1.Text = someObject.ToString();
}
and you can functionalize it as necessary:
public void DisplayObject(Label label, Object someObject)
{
if (someObject == null)
{
label.Text = ""; // or String.Empty, if you're one of *those* people
}
else
{
label.Text = someObject.ToString();
}
}
You could use reflection. Here's an example with one level of nesting:
class Foo
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo
{
Prop1 = (string)null,
Prop2 = (string)null,
Prop3 = (string)null,
};
var props = typeof(Foo).GetProperties()
.Where(x => x.PropertyType == typeof(string));
foreach (var p in props)
{
p.SetValue(foo, string.Empty, null);
}
}
}
You can do that via reflection without too much trouble, and I am sure that by the time I post this there will be answers that tell you exactly how to do that.
But I personally don't like the reflection option.
I prefer to maintain object invariants for all of the object's members through a variety of means. For string members, the invariant is often that it not be null, and sometimes there are maximum length requirements as well (for storage in a database, for example). Other members have other sorts of invariants.
The first step is to create a method that checks all the invariants that you define for the object.
[Conditional("DEBUG")]
private void CheckObjectInvariant()
{
Debug.Assert(name != null);
Debug.Assert(name.Length <= nameMaxLength);
...
}
Then you call this after any method that manipulates the object in any way. Since it is decorated with the ConditionalAttribute, none of these calls will appear in the release version of the application.
Then you just have to make sure that none of the code allows any violations of these invariants. This means that the string fields need to have either initializers in their declarations or they need to be set in all the constructors for the object.
A special problem, and the one that probably motivated this question, is what to do about automatic properties.
public string Name { get; set; }
Obviously, this can be set to null at any time, and there's nothing you can do about that.
There are two options with regard to automatic properties. First, you can just not use them at all. This avoids the problem entirely. Second, you can just allow any possible string value. That is, any code that uses that property has to expect nulls, 10 mb strings or anything in between.
Even if you go with the reflection option to remove nulls, you still have to know when to call the magic-null-removal method on the object to avoid NullReferenceExceptions, so you haven't really bought anything that way.
+1 to Tanascius's answer. I used this answer but tweaked it a bit.
First I only grab the properties that are strings, so it doesn't loop through all my properties. Secondly, I placed in it my BaseEntity class that all my entities inherit from, which makes it global, so I don't have to put it on all my Entities.
public class BaseEntity
{
public int Id { get; set; }
public BaseEntity()
{
var stringProperties = this.GetType().GetProperties().Where(x => x.PropertyType == typeof(string));
foreach (var property in stringProperties)
{
if (property.GetValue(this, null) == null)
{
property.SetValue(this, string.Empty, null);
}
}
}
}
I've created generic List and populate with some objects. Then List I mentioned before converted into DataTable to use in DataGridView. Problem is when I want get Row from this grid I have DataRow. I wanted to convert this to my object againt but not sure how to do it. Maybe you could give some example?
Thanks
Well, if you can't or won't use an "ORM" (object-relational mapper, like Linq-to-SQL or NHibernate - that's exactly what these tools do, and do quite well for you), you'll have to do this yourself.
Converting a DataRow into a domain object model is pretty boring code, really:
public Customer ConvertRowToCustomer(DataRow row)
{
Customer result = new Customer();
result.ID = row.Field<int>("ID");
result.Name = row.Field<string>("CustomerName");
..... // and so on
return result;
}
The biggest challenge here is making this rock-solid and handling (or avoiding) all possible errors (like a field being NULL etc.).
Another possibility would be to have a constructor on your domain model object type that would take a DataRow as parameter and construct a new object from it.
Marc
Assuming you're using a class MyObject, defined as follows :
class MyObject
{
public string Foo { get; set; }
public int Foo { get; set; }
}
You could do something like that :
using System.Data.DataSetExtensions;
...
List<MyObject> list = (from row in table.AsEnumerable()
select new MyObject
{
Foo = row.Field<string>("foo"),
Bar = row.Field<int>("bar")
}).ToList();
Why not just put your objects into a BindingList<> rather than a List<>? Then you can skip the converting to DataTable and back again exercise. You may need to implement INotifyPropertyChanged on your objects, but once they are inside a BindingList, changes in the datagrid will automatically be applied to your underlying objects.
Sorting can be handled by either sorting the list manually on column header click, or by inheriting from BindingList<> and implementing the sorting functionality inside it - then clicking on a header automatically sorts the list - no code required.
Well nowadays it is easier using ORMs of course. But if still you're using the old fashion you can go with a pretty easy Extension Class to do the job for you using a little bit of reflection and generic methods and lambda as follows:
public static class MapperExtensionClass
{
public static IEnumerable<MyClassType> ToMyClassTypeEnumerable(this DataTable table)
{
return table.AsEnumerable().Select(r => r.ToMyClassType());
}
public static MyClassType ToMyClassType(this DataRow row)
{
return row.ToObject<MyClassType>();
}
public static T ToObject<T>(this DataRow row) where T: new()
{
T obj = new T();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (row.Table.Columns.Contains(property.Name))
{
property.SetValue(obj, property.PropertyType.ToDefault(row[property.Name]));
}
}
return obj;
}
public static object ToDefault(this Type type, object obj)
{
if (type == null)
throw new Exception("Customized exception message");
var method = typeof(MapperExtensionClass)
.GetMethod("ToDefaultGeneric", BindingFlags.Static | BindingFlags.Public);
var generic = method.MakeGenericMethod(type);
return generic.Invoke(null, new object[] { obj });
}
public static T ToDefaultGeneric<T>(object obj)
{
if (obj == null || obj == DBNull.Value)
{
return default(T);
}
else
{
return (T)obj;
}
}
}
You should also remember GridView objects can bind a lot of data source types. So it is your decision from a design point about what you should go with.