Click here to Skip to main content
Click here to Skip to main content
Articles » Database » Database » ADO.NET » Downloads
 
Add your own
alternative version
Go to top

Entity Framework in WinForms

, 28 Jul 2014
A component that makes it easy to use Entity Framework in WinForms projects, including design-time binding support.
EF5WinForms.zip
EF5Winforms
EF5WinForms
Design
EntityBindingNavigator.png
EntityDataSource.png
Properties
Resources
Cancel2_small.png
FirstRecord_small.png
LastRecord_small.png
NewItem_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
Sample
BusinessLogic
Model1.edmx
Model1.edmx.diagram
NORTHWND.MDF
NORTHWND_log.ldf
Properties
Settings.settings
Sample.csproj.user
EF6Winforms.zip
EF6Winforms
EF6WinForms
Design
EntityBindingNavigator.png
EntityDataSource.png
Properties
Resources
Cancel2_small.png
FirstRecord_small.png
LastRecord_small.png
NewItem_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
Sample
BusinessLogic
Model1.edmx
Model1.edmx.diagram
NORTHWND.MDF
NORTHWND_log.ldf
Properties
Settings.settings
Sample.csproj.user
efwinforms.zip
EFWinForms
EFWinForms
Design
EntityBindingNavigator.png
EntityDataSource.png
Properties
Resources
Cancel2_small.png
FirstRecord_small.png
LastRecord_small.png
NewItem_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
Sample
BusinessLogic
Forms
Model1.edmx
NORTHWND.MDF
NORTHWND_log.ldf
Properties
DataSources
NORTHWNDEntities.datasource
NORTHWNDEntities1.datasource
Product.datasource
Settings.settings
Resources
Cancel1_small.png
Cancel2_small.png
ChartArea_small.png
Chart_small.png
FirstRecord_small.png
LastRecord_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
Sample.suo
efwinforms_vb.zip
EFWinForms_VB
EFWinForms
EFWinForms.vbproj.user
My Project
Settings.settings
Resources
Cancel2_small.png
FirstRecord_small.png
LastRecord_small.png
NewItem_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
Sample
Model1.edmx
My Project
Application.myapp
DataSources
northwndEntities.datasource
Settings.settings
northwnd.mdf
northwnd_log.ldf
Sample.vbproj.user
Sample.suo
sample.zip
Sample
EFWinForms
Design
EntityBindingNavigator.png
EntityDataSource.png
Properties
Resources
Cancel2_small.png
FirstRecord_small.png
LastRecord_small.png
NewItem_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
Sample
BusinessLogic
Model1.edmx
NORTHWND.MDF
NORTHWND_log.ldf
Properties
DataSources
NORTHWNDEntities.datasource
NORTHWNDEntities1.datasource
Product.datasource
licenses.licx
Settings.settings
Resources
Cancel1_small.png
Cancel2_small.png
ChartArea_small.png
Chart_small.png
FirstRecord_small.png
LastRecord_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
separateconcerns.zip
SeparateConcerns
DataLayer
DataLayer.csproj.user
Model1.edmx
Properties
Settings.settings
EFWinForms
Design
EntityBindingNavigator.png
EntityDataSource.png
Properties
Resources
Cancel2_small.png
FirstRecord_small.png
LastRecord_small.png
NewItem_small.png
NextRecord_small.png
PreviousRecord_small.png
Refresh_small.png
Save_small.png
Undo_small.png
SampleApplication
Properties
DataSources
SeparateConcerns.DataSource.datasource
Settings.settings
SeparateConcerns.suo
using System;
using System.Globalization;
using System.Reflection;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace EFWinforms
{
    /// <summary>
    /// Exposes an ObjectSet in the current ObjectContext as a bindable data source.
    /// </summary>
    /// <remarks>
    /// This class implements the IListSource interface and returns an IBindingList 
    /// built on top of the underlying ObjectSet.
    /// </remarks>
    public class EntitySet :
        IListSource,
        IQueryable
    {
        //-------------------------------------------------------------------------
        #region ** fields

        EntityDataSource _ds;       // EntityDataSource that created this set
        IQueryable _query;          // the value of the property
        IEntityBindingList _list;   // default view for this set
        PropertyInfo _pi;           // the property on the object context that gets the objects in this set
        Type _elementType;          // the type of object in this set
        ListDictionary _dctLookup;  // lookup dictionary (used to show and edit related entities in grid cells)

        #endregion

        //-------------------------------------------------------------------------
        #region ** ctor

        /// <summary>
        /// Initializes a new instance of a <see cref="EntitySet"/>.
        /// </summary>
        /// <param name="ds"><see cref="EntityDataSource"/> that owns the entities.</param>
        /// <param name="pi"><see cref="PropertyInfo"/> used to retrieve the set from the context.</param>
        internal EntitySet(EntityDataSource ds, PropertyInfo pi)
        {
            var type = pi.PropertyType;
            Debug.Assert(
                type.IsGenericType &&
                type.GetGenericTypeDefinition() == typeof(ObjectSet<>) &&
                type.GetGenericArguments().Length == 1);

            _ds = ds;
            _pi = pi;
            _elementType = type.GetGenericArguments()[0];
        }

        #endregion

        //-------------------------------------------------------------------------
        #region ** object model

        /// <summary>
        /// Gets the <see cref="EntityDataSource"/> that owns this entity set.
        /// </summary>
        public EntityDataSource DataSource
        {
            get { return _ds; }
        }
        /// <summary>
        /// Gets the name of this entity set.
        /// </summary>
        public string Name
        {
            get { return _pi != null ? _pi.Name : null; }
        }
        /// <summary>
        /// Gets the type of entity in this entity set.
        /// </summary>
        /// <remarks>
        /// Name chosen for consistency with EntitySet.ElementType 
        /// (EntityType would seem more appropriate).
        /// </remarks>
        public Type ElementType
        {
            get { return _elementType; }
        }
        /// <summary>
        /// Gets the <see cref="IQueryable"/> object that retrieves the entities in this set.
        /// </summary>
        public IQueryable Query
        {
            get 
            {
                if (_query == null && _ds.ObjectContext != null && _pi != null)
                {
                    _query = _pi.GetValue(_ds.ObjectContext, null) as IQueryable;
                }
                return _query; 
            }
        }
        /// <summary>
        /// Gets a list of the entities in the set that have not been deleted or detached.
        /// </summary>
        public IEnumerable ActiveEntities
        {
            get { return GetActiveEntities(Query); }
        }
        /// <summary>
        /// Gets a list of the entities in the set that have not been deleted or detached.
        /// </summary>
        internal static IEnumerable GetActiveEntities(IEnumerable query)
        {
            if (query != null)
            {
                foreach (object item in query)
                {
                    var state = item is EntityObject
                        ? ((EntityObject)item).EntityState
                        : EntityState.Unchanged;
                    switch (state)
                    {
                        case EntityState.Deleted:
                        case EntityState.Detached:
                            break;
                        default:
                            yield return item;
                            break;
                    }
                }
            }
        }
        /// <summary>
        /// Cancels any pending changes on this entity set.
        /// </summary>
        internal void CancelChanges()
        {
            if (_list != null && Query != null)
            {
                _ds.ObjectContext.Refresh(RefreshMode.StoreWins, Query);
                _list.Refresh();
            }
        }
        /// <summary>
        /// Refreshes this set's view by re-loading from the database.
        /// </summary>
        public void RefreshView()
        {
            if (_list != null && Query != null)
            {
                _ds.ObjectContext.Refresh(RefreshMode.ClientWins, Query);
                _list.Refresh();
            }
        }
        /// <summary>
        /// Gets an <see cref="IBindingListView"/> that can be used as a data source for bound controls.
        /// </summary>
        public IBindingListView List
        {
            get { return GetBindingList(); }
        }
        /// <summary>
        /// Gets a dictionary containing entities as keys and their string representation as values.
        /// </summary>
        /// <remarks>
        /// The data map is useful for displaying and editing entities in grid cells.
        /// </remarks>
        public ListDictionary LookupDictionary
        {
            get
            {
                if (_dctLookup == null)
                {
                    _dctLookup = BuildLookupDictionary();
                }
                return _dctLookup;
            }
        }
        #endregion

        //-------------------------------------------------------------------------
        #region ** IListSource

        bool IListSource.ContainsListCollection
        {
            get { return false; }
        }
        IList IListSource.GetList()
        {
            return GetBindingList();
        }

        #endregion

        //-------------------------------------------------------------------------
        #region ** implementation

        // gets an IBindingListView for this entity set
        IBindingListView GetBindingList()
        {
            if (_list == null)
            {
                // create the list
                var listType = typeof(EntityBindingList<>);
                listType = listType.MakeGenericType(this.ElementType);
                _list = (IEntityBindingList)Activator.CreateInstance(listType, _ds, this.Query, Guid.NewGuid().ToString());// this.Name);

                // and listen to changes in the new list
                _list.ListChanged += _list_ListChanged;
            }
            return _list;
        }

        // update data map when list changes
        void _list_ListChanged(object sender, ListChangedEventArgs e)
        {
            if (_dctLookup != null)
            {
                // clear old dictionary
                _dctLookup.Clear();

                // build new dictionary
                var map = BuildLookupDictionary(_list);
                foreach (var kvp in map)
                {
                    _dctLookup.Add(kvp.Key, kvp.Value);
                }
            }
        }

        // build a data map for this entity set
        ListDictionary BuildLookupDictionary()
        {
            return BuildLookupDictionary(ActiveEntities);
        }
        ListDictionary BuildLookupDictionary(IEnumerable entities)
        {
            // if the entity implements "ToString", then use it
            var mi = _elementType.GetMethod("ToString");
            if (mi != null && mi.DeclaringType == _elementType)
            {
                var list = new List<KVPair>();
                foreach (object item in entities)
                {
                    list.Add(new KVPair(item, item.ToString()));
                }
                return BuildLookupDictionary(list);
            }

            // use "DefaultProperty"
            var atts = _elementType.GetCustomAttributes(typeof(DefaultPropertyAttribute), false);
            if (atts != null && atts.Length > 0)
            {
                var dpa = atts[0] as DefaultPropertyAttribute;
                var pi = _elementType.GetProperty(dpa.Name);
                if (pi != null && pi.PropertyType == typeof(string))
                {
                    var list = new List<KVPair>();
                    foreach (object item in entities)
                    {
                        list.Add(new KVPair(item, (string)pi.GetValue(item, null)));
                    }
                    return BuildLookupDictionary(list);
                }
            }

            // no default property: look for properties of type string with 
            // names that contain "Name" or "Description"
            foreach (var pi in _elementType.GetProperties())
            {
                if (pi.PropertyType == typeof(string))
                {
                    if (pi.Name.IndexOf("Name", StringComparison.OrdinalIgnoreCase) > -1 ||
                        pi.Name.IndexOf("Description", StringComparison.OrdinalIgnoreCase) > -1)
                    {
                        var list = new List<KVPair>();
                        foreach (object item in entities)
                        {
                            list.Add(new KVPair(item, (string)pi.GetValue(item, null)));
                        }
                        return BuildLookupDictionary(list);
                    }
                }
            }

            // no dice
            return null;
        }
        ListDictionary BuildLookupDictionary(List<KVPair> list)
        {
            // sort list display value
            list.Sort();

            // create data map
            var map = new ListDictionary();
            foreach (var kvp in list)
            {
                map.Add(kvp.Key, kvp.Value);
            }

            // done
            return map;
        }
        class KVPair : IComparable
        {
            public KVPair(object key, string value)
            {
                Key = key;
                Value = value;
            }
            public object Key { get; set; }
            public string Value { get; set; }
            int IComparable.CompareTo(object obj)
            {
 	            return string.Compare(this.Value, ((KVPair)obj).Value, StringComparison.OrdinalIgnoreCase);
            }
        }

        #endregion

        //-------------------------------------------------------------------------
        #region ** IQueryable

        Type IQueryable.ElementType
        {
            get { return _elementType; }
        }
        Expression IQueryable.Expression
        {
            get { return Query.Expression; }
        }
        IQueryProvider IQueryable.Provider
        {
            get { return Query.Provider; }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return Query.GetEnumerator();
        }

        #endregion
    }
    /// <summary>
    /// Collection of EntitySet objects.
    /// </summary>
    public class EntitySetCollection : ObservableCollection<EntitySet>
    {
        public EntitySet this[string name]
        {
            get
            {
                var index = this.IndexOf(name);
                return index > -1 ? this[index] : null;
            }
        }
        public bool Contains(string name)
        {
            return IndexOf(name) > -1;
        }
        public int IndexOf(string name)
        {
            for (int i = 0; i < Count; i++)
            {
                if (this[i].Name == name)
                    return i;
            }
            return -1;
        }
    }
    /// <summary>
    /// Dictionary that implements IListSource (used for implementing lookup dictionaries)
    /// </summary>
    public class ListDictionary : Dictionary<object, string>, IListSource
    {
        public bool ContainsListCollection
        {
            get { throw new NotImplementedException(); }
        }
        public IList GetList()
        {
            var list = new List<KeyValuePair<object, string>>();
            foreach (var item in this)
            {
                list.Add(item);
            }
            return list;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Bernardo Castilho
Chief Technology Officer ComponentOne
United States United States
No Biography provided

| Advertise | Privacy | Mobile
Web03 | 2.8.140905.1 | Last Updated 28 Jul 2014
Article Copyright 2011 by Bernardo Castilho
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid