I wrote a nifty method to deep copy any object. It does so by recursively calling MemberwiseClone() on any reference type field inside the instance. This method works perfectly on any object I care to use it, including hierarchy relationship objects. The method also sports a dictionary of past visitations so unnecessary duplicate work is avoided.
The problem I am having, however, is that this method only works when the object is NOT data bound to WPF/MVVM when a clone is needed. When data bound and the method is invoked, I run into stack overflow exceptions because of (I assume) an established link between INotifyPropertyChanged.PropertyChanged event and the WPF framework. The recursive call then attempts to copy the entire universe of objects including the AppDomain and low-level Pointer objects, which appear to be linked and go on to almost infinity (more than VS2012 can handle, anyway.)
I doubt I will ever have need to deep copy an object graph that goes back to the beginning of the AppDomain... is there a smart way to have my copy method "stop" when it reaches a certain boundary? I also thought about simply copying objects before they're data bound, but I'm not sure that's a feasible option, and it's rather silly. I just want a simple deep copy solution that works on types that are not serializable, but are also data-bound via INotifyPropertyChanged.
The method's implementation:
private static object Clone(object instance, IDictionary<object, object> visitGraph)
{
var instanceType = instance.GetType();
Debug.WriteLine(instanceType.Name);
object clonedInstance = null;
if (visitGraph.ContainsKey(instance))
{
clonedInstance = visitGraph[instance];
}
else
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance;
var memberwiseCloneMethod =
instanceType.GetMethods(flags).Single(x => x.Name == "MemberwiseClone" &&
!x.GetParameters().Any());
clonedInstance = memberwiseCloneMethod.Invoke(instance, null);
visitGraph.Add(instance, clonedInstance);
var allReferenceTypeProperties = clonedInstance.GetType().GetAllFields()
.Where(
x =>
!x.FieldType.IsValueType
&& x.FieldType != typeof (string));
foreach (var field in allReferenceTypeProperties)
{
var existingFieldValue = field.GetValue(instance);
if (existingFieldValue != null)
{
var clonedFieldValue = Clone(existingFieldValue, visitGraph);
field.SetValue(clonedInstance, clonedFieldValue);
}
}
}
return clonedInstance;
}
public static IEnumerable<FieldInfo> GetAllFields(this Type type)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance;
var fields = type.GetFields(flags);
foreach (var field in fields)
{
yield return field;
}
if (type.BaseType != null)
{
foreach (var field in GetAllFields(type.BaseType))
{
yield return field;
}
}
}
public static object Copy(this object instance)
{
if (instance == null) throw new ArgumentNullException("instance");
var visitGraph = new Dictionary<object, object>();
var clonedInstance = Clone(instance, visitGraph);
return clonedInstance;
}
I'm answering my own question with a shortcut solution. I would much prefer a permanent solution, but I haven't the luxury.
I modified my deep clone method to shallow (instead of deep) copy EventHandler objects. The objects are no longer a true deep clone, but that's not a problem for my application. Still, I would like a solution that applies in all situations.
Related
Using datagridview bound to BindingSource control bound to a LINQ to SQL class, I wonder how to position the bindingSource to a specific record, that is, when I type a Product name in a textbox, the bindingsource should move to that specific product. Here is my code:
In my form FrmFind:
NorthwindDataContext dc;
private void FrmFind_Load(object sender, EventArgs e)
{
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
int index = productBindingSource.Find("ProductName", tb.Text);
if (index >= 0)
{
productBindingSource.Position = index;
}
}
In the program class:
public class FindAbleBindingList<T> : BindingList<T>
{
public FindAbleBindingList()
: base()
{
}
public FindAbleBindingList(List<T> list)
: base(list)
{
}
protected override int FindCore(PropertyDescriptor property, object key)
{
for (int i = 0; i < Count; i++)
{
T item = this[i];
//if (property.GetValue(item).Equals(key))
if (property.GetValue(item).ToString().StartsWith(key.ToString()))
{
return i;
}
}
return -1; // Not found
}
}
How can I implement the find method to make it work?
You can combine the BindingSource.Find() method with the Position property.
For example, if you have something like this in your TextBox changed event handler:
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
int index = bs.Find("Product", tb.Text);
if (index >= 0)
{
bs.Position = index;
}
}
This of course will depend on a lot of things like the particular implementation of the Find method the data source for the binding source has.
In a question you asked a little while ago I gave you an implementation for Find which worked with full matches. Below is a slightly different implementation that will look at the start of the property being inspected:
protected override int FindCore(PropertyDescriptor property, object key)
{
// Simple iteration:
for (int i = 0; i < Count; i++)
{
T item = this[i];
if (property.GetValue(item).ToString().StartsWith(key.ToString()))
{
return i;
}
}
return -1; // Not found
}
Do note that the above method is case sensitive - you can change StartsWith to be case insensitive if you need.
One key thing to note about the way .Net works is that the actual type of an object is not sufficient all the time - the declared type is what consuming code knows about.
This is the reason why you get a NotSupported exception when calling the Find method, even though your BindingList implementation has a Find method - the code that receives this binding list doesn't know about the Find.
The reason for that is in these lines of code:
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
When you set the data source for the binding source you include the extension method OrderBy - Checking this shows that it returns IOrderedEnumerable, an interface described here on MSDN. Note that this interface has no Find method, so even though the underlying FindableBindingList<T> supports Find the binding source doesn't know about it.
There are several solutions (the best is in my opinion to extend your FindableBindingList to also support sorting and sort the list) but the quickest for your current code is to sort earlier like so:
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).OrderBy(p => p.ProductName).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list;
In WinForms there are no entirely out of the box solutions for the things you are trying to do - they all need a little bit of custom code that you need to put together to match just your own requirements.
I took a different approach. I figured, programmatically, every record must be checked until a match is found, so I just iterated using the MoveNext method until I found a match. Unsure if the starting position would be the First record or not, so I used the MoveFirst method to ensure that is was.
There is one assumption, and that is that what you are searching for is unique in that column. In my case, I was looking to match an Identity integer.
int seekID;
this.EntityTableBindingSource.MoveFirst();
if (seekID > 0)
{
foreach (EntityTable sd in EntityTableBindingSource)
{
if (sd.ID != seekID)
{
this.t_EntityTableBindingSource.MoveNext();
}
else
{
break;
}
}
}
I didn't really care for either answer provided. Here is what I came up with for my problem:
// Create a list of items in the BindingSource and use labda to find your row:
var QuickAccessCode = customerListBindingSource.List.OfType<CustomerList>()
.ToList().Find(f => f.QuickAccessCode == txtQAC.Text);
// Then use indexOf to find the object in your bindingSource:
var pos = customerListBindingSource.IndexOf(QuickAccessCode);
if (pos < 0)
{
MessageBox.Show("Could not find " + txtQAC.Text);
}
else
{
mainFrm.customerListBindingSource.Position = pos;
}
So I'm still trying to get my head around Flex and OOP and I am stuck right now. Here is the code I'm currently working with.
var labs:ArrayCollection = new ArrayCollection();
var sets:ArrayCollection = new ArrayCollection();
var labsArray:Array = ["ProDPI","WHCC","Tin"];
var setsArray:Array = [ ["Set01","Set02","Set03","Set04"],["Set11","Set12","Set13","Set14"], ["Set21","Set22","Set23","Set24"] ];
var objLab:Object = new Object;
objLab.labName = labsArray[0];
objLab.setFolders = undefined;
labs.addItem(objLab);
for (var i:int = 0; i < setsArray.length; i++) {
var objSets:Object = new Object;
objSets.setName = setsArray[i];
sets.addItem(objSets);
objLab.setFolders = objSets;
}
labFolderList.labelField="labName";
labFolderList.dataProvider=labs;
setFolderList.labelField="setFolders";
setFolderList.dataProvider=sets;
The objLab object is returning as I wish it to. The objSets is displaying in my ComboBox as object, Object. The application is ComboBox #1 will be populated by labsArray, which is working. Depending on the selection of [0,1,2] from the array this will call from the sets array the array in the matching position.
Finally, with the selection of the ComboBox#1, the second ComboBox#2 will change to the matching selection.
Any help on how to get the object Objects to rendering correctly would be of great help. Also if my execution is not the best way to do this any direction (links/documentation) I will take as well. Thanks in advance.
I edited your original code based on assumptions for what you were trying to accomplish, I added the Array objects directly to the collection being assigned the dataprovider, in this case you should see the output of the .toString() method called on the Array instead of the Object class, the Object class .toString() will output the [object Object] you were seeing in the display. The Array .toString() method will call .toString() on each element in the array and separate them with commas and surround them with curly braces, very similar to what you have when defining the array. Alternatively you could create your own class that extends Object and make your own toString method to override the default behavior, using Object is generally not a great practice (there are exceptions to this it's not a hard rule but it tends to be better to use a specific class type, or even better an interface when possible).
var labs:ArrayCollection = new ArrayCollection();
var sets:ArrayCollection = new ArrayCollection();
var labsArray:Array = ["ProDPI","WHCC","Tin"];
var setsArray:Array = [ ["Set01","Set02","Set03","Set04"],["Set11","Set12","Set13","Set14"], ["Set21","Set22","Set23","Set24"] ];
var objLab:Object = new Object;
objLab.labName = labsArray[0];
objLab.setFolders = undefined;
labs.addItem(objLab);
for (var i:int = 0; i < setsArray.length; i++) {
sets.addItem(setsArray[i]);
objLab.setFolders = setsArray[i];
}
labFolderList.labelField="labName";
labFolderList.dataProvider=labs;
setFolderList.labelField="setFolders";
setFolderList.dataProvider=sets;
Another alternative aside from making a Class as I explained above is to use the labelFunction instead of the labelField, using a labelFunction each item:Object in the dataProvider will be passed into your custom labelFunction and you can return a string based on whatever logic you see fit. It would be something along these lines:
setFolderList.labelFunction = myLabelFunction;
private function myLabelFunction(item:Object):String
{
var retString:String = "";
for(var i:int=0; i<item.setName.length; i++)
{
if(i>0)
retString += ", ";
retString += item.setName[i];
}
return retString;
}
Class C implements INotifyPropertyChanged.
Assume the C has Length, Width and Area propreties, where Area = Length * Width. A change in either might cause a change in area. All three are bound, i.e. the UI expects all three to notify of changes in their values.
When either Length or Width change, their setters call NotifyPropertyChanged.
How should I treat the calculated Area property? Currently the pattern I can think of is detecting in NotifyPropertyChanged whether the changed property is either Length or Width and, if such is the case, initiate an addional PropertyChanged notification for Area. This, however, requires that I maintain inside NotifyPropertyChanged the dependencies graph, which I feel is an anti-pattern.
So, my question is: How should I code dependency properties that depend on other dependency properties?
edit: People here suggested that Length and Width also call NotifyPropertyChanged for Area. Again, I think this is an anti-pattern. A property (IMHO) shouldn't be aware of who depends on it, as shouldn't NotifyPropertyChanged. Only the property should be aware of who it depends on.
This issue kept on bugging me, so I re-opened it.
First, I'd like to appologize for anyone taking my "anti-pattern" comment personally. The solutions offered here were, indeed, how-it's-done in WPF. However, still, IMHO they're bad practices caused, deficiencies in ther framework.
My claim is that the information hiding guide dictates that when B depeneds on A, A should not be aware of B. For exmaple, when B derives from A, A should not have code saying: "If my runtime type is really a B, then do this and that". Simiarily, when B uses A, A should not have code saying: "If the object calling this method is a B, then ..."
So it follows that if property B depends on property A, A shouldn't be the one who's responsible to alert B directly.
Conversely, maintaining (as I currently do) the dependencies graph inside NotifyPropertyChanged is also an anti-pattern. That method should be lightweight and do what it name states, not maintain dependency relationships between properties.
So, I think the solution needed is through aspect oriented programming: Peroperty B should use an "I-depend-on(Property A)" attribute, and some code-rewriter should create the dependency graph and modify NotifyPropertyChanged transparently.
Today, I'm a single programmer working on a single product, so I can't justify dvelving with this any more, but this, I feel, is the correct solution.
Here is an article describing how to create a custom attribute that automatically calls PropertyChanged for properties depending on another property: http://www.redmountainsw.com/wordpress/2012/01/17/a-nicer-way-to-handle-dependent-values-on-propertychanged/
The code will look like this:
[DependsOn("A")]
[DependsOn("B")]
public int Total
{
get { return A + B; }
}
public int A
{
get { return m_A; }
set { m_A = value; RaisePropertyChanged("A"); }
}
public int B
{
get { return m_B: }
set { m_B = value; RaisePropertyChanged("B"); }
}
I haven't tried it myself but I like the idea
When the Length or Width properties are changed you fire PropertyChanged for Area in addition to firing it for either Length or Width.
Here is a very simple implementation based on backing fields and the method OnPropertyChanged to fire the PropertyChanged event:
public Double Length {
get { return this.length; }
set {
this.length = value;
OnPropertyChanged("Length");
OnPropertyChanged("Area");
}
}
public Double Width {
get { return this.width; }
set {
this.width = value;
OnPropertyChanged("Width");
OnPropertyChanged("Area");
}
}
public Double Area {
get { return this.length*this.width; }
}
Doing it like this is certainly not an anti-pattern. That is exactly the pattern for doing it. You as the implementer of the class knows that when Length is changed then Area is also changed and you encode it by raising the appropriate event.
Then you should raise twice, in Length and Width property setters. One for the actual property and one for the Area property.
for example:
private int _width;
public int Width
{
get { return _width; }
set
{
if (_width == value) return;
_width = value;
NotifyPropertyChanged("Width");
NotifyPropertyChanged("Area");
}
}
People here suggested that Length and Width also call
NotifyPropertyChanged for Area. Again, I think this is an
anti-pattern. A property (IMHO) shouldn't be aware of who depends on
it, as shouldn't NotifyPropertyChanged. Only the property should be
aware of who it depends on.
This is not an anti-pattern. Actually, your data encapsulated inside this class, so this class knows when and what changed. You shouldn't know outside of this class that Area depends on Width and Length. So the most logical place to notify listeners about Area is the Width and Length setter.
A property (IMHO) shouldn't be aware of who depends on it, as
shouldn't NotifyPropertyChanged.
It does not break encapsulation, because you are in the same class, in the same data structure.
An extra information is that knockout.js (a javascript mvvm library) has a concept which accessing this problem: Computed Observables. So I believe this is absolutely acceptable.
Here is a possible implementation of an attribute:
public class DependentPropertiesAttribute : Attribute
{
private readonly string[] properties;
public DependentPropertiesAttribute(params string[] dp)
{
properties = dp;
}
public string[] Properties
{
get
{
return properties;
}
}
}
Then in the Base View Model, we handle the mechanism of calling property dependencies:
public class ViewModelBase : INotifyPropertyChanged
{
public ViewModelBase()
{
DetectPropertiesDependencies();
}
private readonly Dictionary<string, List<string>> _dependencies = new Dictionary<string, List<string>>();
private void DetectPropertiesDependencies()
{
var propertyInfoWithDependencies = GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(DependentPropertiesAttribute))).ToArray();
foreach (PropertyInfo propertyInfo in propertyInfoWithDependencies)
{
var ca = propertyInfo.GetCustomAttributes(false).OfType<DependentPropertiesAttribute>().Single();
if (ca.Properties != null)
{
foreach (string prop in ca.Properties)
{
if (!_dependencies.ContainsKey(prop))
{
_dependencies.Add(prop, new List<string>());
}
_dependencies[prop].Add(propertyInfo.Name);
}
}
}
}
protected void OnPropertyChanged(params Expression<Func<object>>[] expressions)
{
expressions.Select(expr => ReflectionHelper.GetPropertyName(expr)).ToList().ForEach(p => {
RaisePropertyChanged(p);
RaiseDependentProperties(p, new List<string>() { p });
});
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected void RaiseDependentProperties(string propertyName, List<string> calledProperties = null)
{
if (!_dependencies.Any() || !_dependencies.ContainsKey(propertyName))
return;
if (calledProperties == null)
calledProperties = new List<string>();
List<string> dependentProperties = _dependencies[propertyName];
foreach (var dependentProperty in dependentProperties)
{
if (!calledProperties.Contains(dependentProperty))
{
RaisePropertyChanged(dependentProperty);
RaiseDependentProperties(dependentProperty, calledProperties);
}
}
}
}
Finally we define dependencies in our ViewModel
[DependentProperties("Prop1", "Prop2")]
public bool SomeCalculatedProperty
{
get
{
return Prop1 + Prop2;
}
}
I want to share an Array which all classes can "get" and "change" data inside that array. Something like a Global array or Multi Access array. How this is possible with ActionScript 3.0 ?
There are a couple of ways to solve this. One is to use a global variable (as suggested in unkiwii's answer) but that's not a very common approach in ActionScript. More common approaches are:
Class variable (static variable)
Create a class called DataModel or similar, and define an array variable on that class as static:
public class DataModel {
public static var myArray : Array = [];
}
You can then access this from any part in your application using DataModel.myArray. This is rarely a great solution because (like global variables) there is no way for one part of your application to know when the content of the array is modified by another part of the application. This means that even if your data entry GUI adds an object to the array, your data list GUI will not know to show the new data, unless you implement some other way of telling it to redraw.
Singleton wrapping array
Another way is to create a class called ArraySingleton, which wraps the actual array and provides access methods to it, and an instance of which can be accessed using the very common singleton pattern of keeping the single instance in a static variable.
public class ArraySingleton {
private var _array : Array;
private static var _instance : ArraySingleton;
public static function get INSTANCE() : ArraySingleton {
if (!_instance)
_instance = new ArraySingleton();
return _instance;
}
public function ArraySingleton() {
_array = [];
}
public function get length() : uint {
return _array.length;
}
public function push(object : *) : void {
_array.push(object);
}
public function itemAt(idx : uint) : * {
return _array[idx];
}
}
This class wraps an array, and a single instance can be accessed through ArraySingleton.INSTANCE. This means that you can do:
var arr : ArraySingleton = ArraySingleton.INSTANCE;
arr.push('a');
arr.push('b');
trace(arr.length); // traces '2'
trace(arr.itemAt(0)); // trace 'a'
The great benefit of this is that you can dispatch events when items are added or when the array is modified in any other way, so that all parts of your application can be notified of such changes. You will likely want to expand on the example above by implementing more array-like interfaces, like pop(), shift(), unshift() et c.
Dependency injection
A common pattern in large-scale application development is called dependency injection, and basically means that by marking your class in some way (AS3 meta-data is often used) you can signal that the framework should "inject" a reference into that class. That way, the class doesn't need to care about where the reference is coming from, but the framework will make sure that it's there.
A very popular DI framework for AS3 is Robotlegs.
NOTE: I discourage the use of Global Variables!
But here is your answer
You can go to your default package and create a file with the same name of your global variable and set the global variable public:
//File: GlobalArray.as
package {
public var GlobalArray:Array = [];
}
And that's it! You have a global variable. You can acces from your code (from anywhere) like this:
function DoSomething() {
GlobalArray.push(new Object());
GlobalArray.pop();
for each (var object:* in GlobalArray) {
//...
}
}
As this question was linked recently I would add something also. I was proposed to use singleton ages ago and resigned on using it as soon as I realized how namespaces and references work and that having everything based on global variables is bad idea.
Aternative
Note this is just a showcase and I do not advice you to use such approach all over the place.
As for alternative to singleton you could have:
public class Global {
public static const myArray:Alternative = new Alternative();
}
and use it almost like singleton:
var ga:Alternative = Global.myArray;
ga.e.addEventListener(GDataEvent.NEW_DATA, onNewData);
ga.e.addEventListener(GDataEvent.DATA_CHANGE, onDataChange);
ga.push(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "ten");
trace(ga[5]); // 5
And your Alternative.as would look similar to singleton one:
package adnss.projects.tchqs
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class Alternative extends Proxy
{
private var _data:Array = [];
private var _events:AltEventDisp = new AltEventDisp();
private var _dispatching:Boolean = false;
public var blockCircularChange:Boolean = true;
public function Alternative() {}
override flash_proxy function getProperty(id:*):* {var i:int = id;
return _data[i += (i < 0) ? _data.length : 0];
//return _data[id]; //version without anal item access - var i:int could be removed.
}
override flash_proxy function setProperty(id:*, value:*):void { var i:int = id;
if (_dispatching) { throw new Error("You cannot set data while DATA_CHANGE event is dipatching"); return; }
i += (i < 0) ? _data.length : 0;
if (i > 9 ) { throw new Error ("You can override only first 10 items without using push."); return;}
_data[i] = value;
if (blockCircularChange) _dispatching = true;
_events.dispatchEvent(new GDataEvent(GDataEvent.DATA_CHANGE, i));
_dispatching = false;
}
public function push(...rest) {
var c:uint = -_data.length + _data.push.apply(null, rest);
_events.dispatchEvent(new GDataEvent(GDataEvent.NEW_DATA, _data.length - c, c));
}
public function get length():uint { return _data.length; }
public function get e():AltEventDisp { return _events; }
public function toString():String { return String(_data); }
}
}
import flash.events.EventDispatcher;
/**
* Dispatched after data at existing index is replaced.
* #eventType adnss.projects.tchqs.GDataEvent
*/
[Event(name = "dataChange", type = "adnss.projects.tchqs.GDataEvent")]
/**
* Dispatched after new data is pushed intwo array.
* #eventType adnss.projects.tchqs.GDataEvent
*/
[Event(name = "newData", type = "adnss.projects.tchqs.GDataEvent")]
class AltEventDisp extends EventDispatcher { }
The only difference form Singleton is that you can actually have multiple instances of this class so you can reuse it like this:
public class Global {
public static const myArray:Alternative = new Alternative();
public static const myArray2:Alternative = new Alternative();
}
to have two separated global arrays or even us it as instance variable at the same time.
Note
Wrapping array like this an using methods like myArray.get(x) or myArray[x] is obviously slower than accessing raw array (see all additional steps we are taking at setProperty).
public static const staticArray:Array = [1,2,3];
On the other hand you don't have any control over this. And the content of the array can be changed form anywhere.
Caution about events
I would have to add that if you want to involve events in accessing data that way you should be careful. As with every sharp blade it's easy to get cut.
For example consider what happens when you do this this:
private function onDataChange(e:GDataEvent):void {
trace("dataChanged at:", e.id, "to", Global.myArray[e.id]);
Global.myArray[e.id]++;
trace("new onDataChange is called before function exits");
}
The function is called after data in array was changed and inside that function you changing the data again. Basically it's similar to doing something like this:
function f(x:Number) {
f(++x);
}
You can see what happens in such case if you toggle myArray.blockCircularChange. Sometimes you would intentionally want to have such recursion but it is likely that you will do it "by accident". Unfortunately flash will suddenly stop such events dispatching without even telling you why and this could be confusing.
Download full example here
Why using global variables is bad in most scenarios?
I guess there is many info about that all over the internet but to be complete I will add simple example.
Consider you have in your app some view where you display some text, or graphics, or most likely game content. Say you have chess game. Mayby you have separated logic and graphics in two classes but you want both to operate on the same pawns. So you create your Global.pawns variable and use that in both Grahpics and Logic class.
Everything is randy-dandy and works flawlessly. Now You come with the great idea - add option for user to play two matches at once or even more. All you have to do is to create another instance of your match... right?
Well you are doomed at this point because, every single instance of your class will use the same Global.pawns array. You not only have this variable global but also you have limited yourself to use only single instance of each class that use this variable :/
So before you use any global variables, just think twice if the thing you want to store in it is really global and universal across your entire app.
I have a List<T> bound to a datagrid and am trying to figure out how to save all the changes to the database (using Entity Framework), not just one row at a time; OR, at least a better way to commit changes from the datagrid to the database. I am using the MVVM pattern.
Here is what I have as far as saving one row:
private static PROJECT_EXPENSEEntities _service;
//The default constructor
public ProjectExpenseItemsRepository()
{
if (_service == null)
{
_service = new PROJECT_EXPENSEEntities();
}
}
public void AddProjectExpenseItems(int projectExpenseID)
{
project_expense_items p = new project_expense_items();
if (entityList != null)
{
p.project_expense_id = projectExpenseID;
p.item_number = entityList.ItemNumber;
p.item_description = entityList.ItemDescription;
p.item_unit_price = entityList.ItemUnitPrice;
p.item_qty = entityList.ItemQty;
p.supplier_name = entityList.SupplierName;
p.create_date = System.DateTime.Today;
_service.AddObject("project_expense_items", p);
_service.SaveChanges();
}
}
However, I would prefer to send the changes to all rows in the datagrid at once:
public void AddProjectExpenseItems(List<ProjectExpenseItemsBO> entityList, int projectExpenseID)
{
project_expense_items p = new project_expense_items();
if (entityList != null)
{
// not sure what goes here, do I need a loop for each item in the list?
// I have a feeling I am going in the wrong direction with this...
_service.AddObject("project_expense_items", entityList);
_service.SaveChanges();
}
}
I haven't found many good examples on the web. If someone could point me to an example I'd appreciate it.
Yes. You can simply loop through your list and SaveChanges once after adding/modifying/deleting the items.
Of course, you would likely want to load and display items from the database, so binding directly to EntityCollection<project_expense_items> _service.project_expense_items would be more effective than using a list of detached entities. Entity Framework will track changes for you.
See this: http://msdn.microsoft.com/en-us/library/bb896269.aspx