I'm trying to take advantage of Bidirectional serialization of some relational Linq-2-Sql generated entity classes. When using Unidirectional option everything works just fine, bu the moment I add IsReferenceType=true, objects fail to get transported over the tcp binding.
Sample code:
Entity class:
[Table(Name="dbo.Blocks")]
[DataContract()]
public partial class Block : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private long _ID;
private int _StatusID;
private string _Name;
private bool _IsWithControlPoints;
private long _DivisionID;
private string _SHAPE;
private EntitySet<BlockByWorkstation> _BlockByWorkstations;
private EntitySet<PlanningPointAppropriation> _PlanningPointAppropriations;
private EntitySet<Neighbor> _Neighbors;
private EntitySet<Neighbor> _Neighbors1;
private EntitySet<Task> _Tasks;
private EntitySet<PlanningPointByBlock> _PlanningPointByBlocks;
private EntitySet<ControlPointByBlock> _ControlPointByBlocks;
private EntityRef<Division> _Division;
private bool serializing;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnIDChanging(long value);
partial void OnIDChanged();
partial void OnStatusIDChanging(int value);
partial void OnStatusIDChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();
partial void OnIsWithControlPointsChanging(bool value);
partial void OnIsWithControlPointsChanged();
partial void OnDivisionIDChanging(long value);
partial void OnDivisionIDChanged();
partial void OnSHAPEChanging(string value);
partial void OnSHAPEChanged();
#endregion
public Block()
{
this.Initialize();
}
[Column(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="BigInt NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
[DataMember(Order=1)]
public override long ID
{
get
{
return this._ID;
}
set
{
if ((this._ID != value))
{
this.OnIDChanging(value);
this.SendPropertyChanging();
this._ID = value;
this.SendPropertyChanged("ID");
this.OnIDChanged();
}
}
}
[Column(Storage="_StatusID", DbType="Int NOT NULL")]
[DataMember(Order=2)]
public int StatusID
{
get
{
return this._StatusID;
}
set
{
if ((this._StatusID != value))
{
this.OnStatusIDChanging(value);
this.SendPropertyChanging();
this._StatusID = value;
this.SendPropertyChanged("StatusID");
this.OnStatusIDChanged();
}
}
}
[Column(Storage="_Name", DbType="NVarChar(255)")]
[DataMember(Order=3)]
public string Name
{
get
{
return this._Name;
}
set
{
if ((this._Name != value))
{
this.OnNameChanging(value);
this.SendPropertyChanging();
this._Name = value;
this.SendPropertyChanged("Name");
this.OnNameChanged();
}
}
}
[Column(Storage="_IsWithControlPoints", DbType="Bit NOT NULL")]
[DataMember(Order=4)]
public bool IsWithControlPoints
{
get
{
return this._IsWithControlPoints;
}
set
{
if ((this._IsWithControlPoints != value))
{
this.OnIsWithControlPointsChanging(value);
this.SendPropertyChanging();
this._IsWithControlPoints = value;
this.SendPropertyChanged("IsWithControlPoints");
this.OnIsWithControlPointsChanged();
}
}
}
[Column(Storage="_DivisionID", DbType="BigInt NOT NULL")]
[DataMember(Order=5)]
public long DivisionID
{
get
{
return this._DivisionID;
}
set
{
if ((this._DivisionID != value))
{
if (this._Division.HasLoadedOrAssignedValue)
{
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
this.OnDivisionIDChanging(value);
this.SendPropertyChanging();
this._DivisionID = value;
this.SendPropertyChanged("DivisionID");
this.OnDivisionIDChanged();
}
}
}
[Column(Storage="_SHAPE", DbType="Text", UpdateCheck=UpdateCheck.Never)]
[DataMember(Order=6)]
public string SHAPE
{
get
{
return this._SHAPE;
}
set
{
if ((this._SHAPE != value))
{
this.OnSHAPEChanging(value);
this.SendPropertyChanging();
this._SHAPE = value;
this.SendPropertyChanged("SHAPE");
this.OnSHAPEChanged();
}
}
}
[Association(Name="Block_BlockByWorkstation", Storage="_BlockByWorkstations", ThisKey="ID", OtherKey="BlockID")]
[DataMember(Order=7, EmitDefaultValue=false)]
public EntitySet<BlockByWorkstation> BlockByWorkstations
{
get
{
if ((this.serializing
&& (this._BlockByWorkstations.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._BlockByWorkstations;
}
set
{
this._BlockByWorkstations.Assign(value);
}
}
[Association(Name="Block_PlanningPointAppropriation", Storage="_PlanningPointAppropriations", ThisKey="ID", OtherKey="MasterBlockID")]
[DataMember(Order=8, EmitDefaultValue=false)]
public EntitySet<PlanningPointAppropriation> PlanningPointAppropriations
{
get
{
if ((this.serializing
&& (this._PlanningPointAppropriations.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._PlanningPointAppropriations;
}
set
{
this._PlanningPointAppropriations.Assign(value);
}
}
[Association(Name="Block_Neighbor", Storage="_Neighbors", ThisKey="ID", OtherKey="FirstBlockID")]
[DataMember(Order=9, EmitDefaultValue=false)]
public EntitySet<Neighbor> Neighbors
{
get
{
if ((this.serializing
&& (this._Neighbors.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Neighbors;
}
set
{
this._Neighbors.Assign(value);
}
}
[Association(Name="Block_Neighbor1", Storage="_Neighbors1", ThisKey="ID", OtherKey="SecondBlockID")]
[DataMember(Order=10, EmitDefaultValue=false)]
public EntitySet<Neighbor> Neighbors1
{
get
{
if ((this.serializing
&& (this._Neighbors1.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Neighbors1;
}
set
{
this._Neighbors1.Assign(value);
}
}
[Association(Name="Block_Task", Storage="_Tasks", ThisKey="ID", OtherKey="BlockID")]
[DataMember(Order=11, EmitDefaultValue=false)]
public EntitySet<Task> Tasks
{
get
{
if ((this.serializing
&& (this._Tasks.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Tasks;
}
set
{
this._Tasks.Assign(value);
}
}
[Association(Name="Block_PlanningPointByBlock", Storage="_PlanningPointByBlocks", ThisKey="ID", OtherKey="BlockID")]
[DataMember(Order=12, EmitDefaultValue=false)]
public EntitySet<PlanningPointByBlock> PlanningPointByBlocks
{
get
{
if ((this.serializing
&& (this._PlanningPointByBlocks.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._PlanningPointByBlocks;
}
set
{
this._PlanningPointByBlocks.Assign(value);
}
}
[Association(Name="Block_ControlPointByBlock", Storage="_ControlPointByBlocks", ThisKey="ID", OtherKey="BlockID")]
[DataMember(Order=13, EmitDefaultValue=false)]
public EntitySet<ControlPointByBlock> ControlPointByBlocks
{
get
{
if ((this.serializing
&& (this._ControlPointByBlocks.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._ControlPointByBlocks;
}
set
{
this._ControlPointByBlocks.Assign(value);
}
}
[Association(Name="Division_Block", Storage="_Division", ThisKey="DivisionID", OtherKey="ID", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
public Division Division
{
get
{
return this._Division.Entity;
}
set
{
Division previousValue = this._Division.Entity;
if (((previousValue != value)
|| (this._Division.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Division.Entity = null;
previousValue.Blocks.Remove(this);
}
this._Division.Entity = value;
if ((value != null))
{
value.Blocks.Add(this);
this._DivisionID = value.ID;
}
else
{
this._DivisionID = default(long);
}
this.SendPropertyChanged("Division");
}
}
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SendPropertyChanging()
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, emptyChangingEventArgs);
}
}
protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void attach_BlockByWorkstations(BlockByWorkstation entity)
{
this.SendPropertyChanging();
entity.Block = this;
}
private void detach_BlockByWorkstations(BlockByWorkstation entity)
{
this.SendPropertyChanging();
entity.Block = null;
}
private void attach_PlanningPointAppropriations(PlanningPointAppropriation entity)
{
this.SendPropertyChanging();
entity.Block = this;
}
private void detach_PlanningPointAppropriations(PlanningPointAppropriation entity)
{
this.SendPropertyChanging();
entity.Block = null;
}
private void attach_Neighbors(Neighbor entity)
{
this.SendPropertyChanging();
entity.FirstBlock = this;
}
private void detach_Neighbors(Neighbor entity)
{
this.SendPropertyChanging();
entity.FirstBlock = null;
}
private void attach_Neighbors1(Neighbor entity)
{
this.SendPropertyChanging();
entity.SecondBlock = this;
}
private void detach_Neighbors1(Neighbor entity)
{
this.SendPropertyChanging();
entity.SecondBlock = null;
}
private void attach_Tasks(Task entity)
{
this.SendPropertyChanging();
entity.Block = this;
}
private void detach_Tasks(Task entity)
{
this.SendPropertyChanging();
entity.Block = null;
}
private void attach_PlanningPointByBlocks(PlanningPointByBlock entity)
{
this.SendPropertyChanging();
entity.Block = this;
}
private void detach_PlanningPointByBlocks(PlanningPointByBlock entity)
{
this.SendPropertyChanging();
entity.Block = null;
}
private void attach_ControlPointByBlocks(ControlPointByBlock entity)
{
this.SendPropertyChanging();
entity.Block = this;
}
private void detach_ControlPointByBlocks(ControlPointByBlock entity)
{
this.SendPropertyChanging();
entity.Block = null;
}
private void Initialize()
{
this._BlockByWorkstations = new EntitySet<BlockByWorkstation>(new Action<BlockByWorkstation>(this.attach_BlockByWorkstations), new Action<BlockByWorkstation>(this.detach_BlockByWorkstations));
this._PlanningPointAppropriations = new EntitySet<PlanningPointAppropriation>(new Action<PlanningPointAppropriation>(this.attach_PlanningPointAppropriations), new Action<PlanningPointAppropriation>(this.detach_PlanningPointAppropriations));
this._Neighbors = new EntitySet<Neighbor>(new Action<Neighbor>(this.attach_Neighbors), new Action<Neighbor>(this.detach_Neighbors));
this._Neighbors1 = new EntitySet<Neighbor>(new Action<Neighbor>(this.attach_Neighbors1), new Action<Neighbor>(this.detach_Neighbors1));
this._Tasks = new EntitySet<Task>(new Action<Task>(this.attach_Tasks), new Action<Task>(this.detach_Tasks));
this._PlanningPointByBlocks = new EntitySet<PlanningPointByBlock>(new Action<PlanningPointByBlock>(this.attach_PlanningPointByBlocks), new Action<PlanningPointByBlock>(this.detach_PlanningPointByBlocks));
this._ControlPointByBlocks = new EntitySet<ControlPointByBlock>(new Action<ControlPointByBlock>(this.attach_ControlPointByBlocks), new Action<ControlPointByBlock>(this.detach_ControlPointByBlocks));
this._Division = default(EntityRef<Division>);
OnCreated();
}
[OnDeserializing()]
[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)]
public void OnDeserializing(StreamingContext context)
{
this.Initialize();
}
[OnSerializing()]
[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)]
public void OnSerializing(StreamingContext context)
{
this.serializing = true;
}
[OnSerialized()]
[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)]
public void OnSerialized(StreamingContext context)
{
this.serializing = false;
}
}
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service behaviorConfiguration="debugging"
name="DBServicesLibrary.DBService">
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="DBServicesLibrary.DBServiceBehavior">
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="True"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
<behavior name="debugging">
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Host part:
ServiceHost svh = new ServiceHost(typeof(DBService));
svh.AddServiceEndpoint(
typeof(DBServices.Contract.IDBService),
new NetTcpBinding(),
"net.tcp://localhost:8000");
Client part:
ChannelFactory<DBServices.Contract.IDBService> scf;
scf = new ChannelFactory<DBServices.Contract.IDBService>(new NetTcpBinding(),"net.tcp://localhost:8000");
_serv = scf.CreateChannel();
((IContextChannel)_serv).OperationTimeout = new TimeSpan(0, 5, 0);
L2ST4 will help you.
and I quote...
"LINQ to SQL supports the basic DataContract serialization out of the box. This basic serialization had no mechanism to tell it which end of an association was the owner to prevent serialization getting stuck in a loop and so LINQ to SQL would only serialize one side of the relationship."
It's not enough to just add IsReferenceType=true to your DataContract attribute. You also have to make sure all properties have the DataMember attribute. When you set the serialization mode to unidirectional it only automatically adds the DataMember attribute in one direction. For example, it will automatically put the attribute on Group.Users, but you will have to add it to User.Group yourself. I recently tried manually adding in IsReferenceType=true and DataMember and it worked.
Related
I noticed on ListBoxes and LongListSelectors, that the item bound property requests are deferred until they are scrolled toward.
Is it possible to do the same thing in relation to custom positioned items in a ScrollViewer, such that the item doesn't make any request for its bound property values until it is scrolled toward? Is there any easy setting to make this happen automatically?
If not, then what's the simplest way of doing it otherwise?
Its called Lazy Load
here you have an example of Lazy List:
public class LazyCollection<T> : IList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
private const int LOAD_THRESHOLD = 3;
private ObservableCollection<T> _innerCollection;
public LazyCollection(Func<int, IEnumerable<T>> fetch)
: this(fetch, null)
{ }
public LazyCollection(Func<int, IEnumerable<T>> fetch, IEnumerable<T> items)
{
_fetch = fetch;
_innerCollection = new ObservableCollection<T>(items ?? new T[0]);
this.AttachEvents();
this.HasMoreItems = true;
}
private void AttachEvents()
{
_innerCollection.CollectionChanged += (s, e) => OnCollectionChanged(e);
((INotifyPropertyChanged)_innerCollection).PropertyChanged += (s, e) => OnPropertyChanged(e);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, e);
}
#endregion
#region INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (this.CollectionChanged != null)
this.CollectionChanged(this, e);
}
#endregion
#region IList<T>
public int IndexOf(T item)
{
return _innerCollection.IndexOf(item);
}
public void Insert(int index, T item)
{
_innerCollection.Insert(index, item);
}
public void RemoveAt(int index)
{
_innerCollection.RemoveAt(index);
}
public T this[int index]
{
get
{
Debug.WriteLine("LazyCollection - Reading item {0}", index);
return _innerCollection[index];
}
set { _innerCollection[index] = value; }
}
public void Add(T item)
{
_innerCollection.Add(item);
}
public void Clear()
{
_innerCollection.Clear();
}
public bool Contains(T item)
{
return _innerCollection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_innerCollection.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _innerCollection.Count; }
}
public bool IsReadOnly
{
get { return ((IList<T>)_innerCollection).IsReadOnly; }
}
public bool Remove(T item)
{
return _innerCollection.Remove(item);
}
public IEnumerator<T> GetEnumerator()
{
Debug.WriteLine("LazyCollection - GetEnumerator");
return _innerCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
Debug.WriteLine("LazyCollection - GetEnumerator explicit");
return ((IEnumerable)_innerCollection).GetEnumerator();
}
#endregion
#region IList
int IList.Add(object value)
{
return ((IList)_innerCollection).Add(value);
}
bool IList.Contains(object value)
{
return ((IList)_innerCollection).Contains(value);
}
int IList.IndexOf(object value)
{
return ((IList)_innerCollection).IndexOf(value);
}
void IList.Insert(int index, object value)
{
((IList)_innerCollection).Insert(index, value);
}
bool IList.IsFixedSize
{
get { return ((IList)_innerCollection).IsFixedSize; }
}
bool IList.IsReadOnly
{
get { return ((IList)_innerCollection).IsReadOnly; }
}
void IList.Remove(object value)
{
((IList)_innerCollection).Remove(value);
}
object IList.this[int index]
{
get
{
if (index > this.Count - LOAD_THRESHOLD)
{
this.TryLoadMoreItems();
}
Debug.WriteLine("LazyCollection - Reading item {0} IList", index);
return ((IList)_innerCollection)[index];
}
set { ((IList)_innerCollection)[index] = value; }
}
void ICollection.CopyTo(Array array, int index)
{
((IList)_innerCollection).CopyTo(array, index);
}
bool ICollection.IsSynchronized
{
get { return ((IList)_innerCollection).IsSynchronized; }
}
object ICollection.SyncRoot
{
get { return ((IList)_innerCollection).SyncRoot; }
}
#endregion
public T[] GetLoadedItems()
{
return _innerCollection.ToArray();
}
public void ResetLoadedItems(IEnumerable<T> list)
{
_innerCollection.Clear();
foreach (var i in list)
_innerCollection.Add(i);
this.HasMoreItems = true;
}
public bool HasMoreItems { get; set; }
private bool _isLoading = false;
private Func<int, IEnumerable<T>> _fetch;
private async Task TryLoadMoreItems()
{
if (_isLoading || !this.HasMoreItems)
return;
try
{
_isLoading = true;
Debug.WriteLine("LazyCollection - Loading more items skip {0}", this.Count);
List<T> items = _fetch != null ? (_fetch(Count)).ToList() : new List<T>();
if (items.Count == 0)
{
Debug.WriteLine("LazyCollection - No items returned, Loading disabled", this.Count);
this.HasMoreItems = false;
}
items.ForEach(x => _innerCollection.Add(x));
Debug.WriteLine("LazyCollection - Items added. Total count: {0}", this.Count);
}
finally
{
_isLoading = false;
}
}
}
public class SearchResults : LazyCollection<Verse>
{
public SearchResults()
: base(count => GetSearchResult(), GetSearchResult())
{
}
public static int _index = 0;
public static string CurrentSearch = "";
public static List<Verse> AllVerses = new List<Verse>();
private static List<Verse> GetSearchResult()
{
List<Verse> results = new List<Verse>();
string lower = CurrentSearch.ToLower();
bool resultsChanged = false;
for (int index = _index; index < AllVerses.Count; index++)
{
Verse verse = AllVerses[index];
if (verse.Content.ToLower().Contains(lower) || verse.Header.ToLower().Contains(lower))
{
results.Add(verse);
resultsChanged = true;
}
if ((index >= (AllVerses.Count / 200) + _index || index + 1 == AllVerses.Count) && resultsChanged && (results.Count > 10 || AllVerses.Count == index + 1))
{
_index = index + 1;
return results;
}
}
return results;
}
}
private ISessionFactory GetSessionFactory(string sessionFactoryConfigPath)
{
GetFullSessionFactoryFor(sessionFactoryConfigPath);
while (!sessionFactoryReady) Thread.Sleep(1000);
return (ISessionFactory)sessionFactories[sessionFactoryConfigPath];
}
private void GetFullSessionFactory(string sessionFactoryConfigPath)
{
ThreadPool.QueueUserWorkItem(state =>
{
ISessionFactory sessionFactory=null;
FluentConfiguration fluentConfiguration = fluentConfiguration.ExposeConfiguration(c => c.SetProperty("sessionfactoryname","somevalue"))
.Mappings(m =>
{
m.FluentMappings
.AddFromAssembly(Assembly.Load("nameofassembly"))
.Conventions.Add(DefaultLazy.Always(),
OptimisticLock.Is(x => x.All()),
DynamicUpdate.AlwaysTrue(),
DynamicInsert.AlwaysFalse(),
DefaultCascade.None()
)
.Conventions.AddFromAssemblyOf<"SomeConvention">();
}
);
sessionFactory = fluentConfiguration.BuildSessionFactory();
});
}
I am creating minisession factory on main thread(not shown here) and full session factory on second thread.
The problem is when it hits buildsessionfactory the code never returns back.Am i doing it right?
public class NHibernateBaseDAO<T>
{
public NHibernateBaseDAO(string sessionFactoryConfigPath, int sessionId)
{
SessionFactoryConfigPath = sessionFactoryConfigPath;
SessionId = sessionId;
public bool Save(T entity)
{
bool saveSuccessful = true;
try
{
NHibernateSession.Save(entity);
}
catch (NHibernate.HibernateException)
{
saveSuccessful = false;
}
return saveSuccessful;
}
public bool SaveOrUpdate(T entity)
{
bool saveSuccessful = true;
try
{
NHibernateSession.SaveOrUpdate(entity);
}
catch (NHibernate.HibernateException)
{
saveSuccessful = false;
}
return saveSuccessful;
}
public void Delete(T entity)
{
NHibernateSession.Delete(entity);
}
public void CommitChanges()
{
if (NHibernateSessionManager.Instance.HasOpenTransactionOn(SessionFactoryConfigPath, this.SessionId))
{
NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath, this.SessionId).Flush();
NHibernateSessionManager.Instance.CommitTransactionOn(SessionFactoryConfigPath, this.SessionId);
}
else
{
NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath, this.SessionId).Flush();
}
}
public void BeginTransaction()
{
NHibernateSessionManager.Instance.BeginTransactionOn(SessionFactoryConfigPath, this.SessionId);
}
public void RollbackTransaction()
{
NHibernateSessionManager.Instance.RollbackTransactionOn(SessionFactoryConfigPath, this.SessionId);
}
public bool IsDirty()
{
return NHibernateSession.IsDirty();
}
public IQueryable<T> Query() {
return (IQueryable<T>)NHibernateSession.Query<T>();
}
protected ISession NHibernateSession
{
get
{
return NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath, this.SessionId);
}
}
protected readonly string SessionFactoryConfigPath;
protected int SessionId;
protected System.Data.IDbConnection DbConnection
{
get { return NHibernateSessionManager.Instance.GetDbConnection(SessionFactoryConfigPath, this.SessionId); }
}
/// <summary>
/// Return a list of object arrays. use this for general queries
/// </summary>
public System.Collections.IEnumerable GetSqlQuery(string queryString, IList<Criterion> criterion, Type returnType)
{
queryString += CriteriaToSql(criterion);
return NHibernateSession.CreateQuery(queryString).Enumerable();
}
protected ICriteria AddCriteria(IList<Criterion> criterion)
{
ICriteria criteria = NHibernateSession.CreateCriteria(persistentType);
foreach (Criterion criterium in criterion)
{
switch (criterium.Comparison)
{
case SqlComparison.StartsWith:
criteria.Add(Restrictions.InsensitiveLike(criterium.Property, criterium.Value1.ToString(), MatchMode.Start));
break;
case SqlComparison.Contains:
criteria.Add(Restrictions.InsensitiveLike(criterium.Property, criterium.Value1.ToString(), MatchMode.Anywhere));
break;
case SqlComparison.Equals:
criteria.Add(Restrictions.Eq(criterium.Property, criterium.Value1));
break;
case SqlComparison.Between:
criteria.Add(Restrictions.Between(criterium.Property, criterium.Value1, criterium.Value2));
break;
case SqlComparison.MoreThan:
criteria.Add(Restrictions.Gt(criterium.Property, criterium.Value1));
break;
case SqlComparison.LessThan:
criteria.Add(Restrictions.Lt(criterium.Property, criterium.Value2));
break;
case SqlComparison.InList:
criteria.Add(Restrictions.In(criterium.Property, (System.Collections.IList)criterium.Value1));
break;
}
}
return criteria;
}
protected string CriteriaToSql(IList<Criterion> criterion)
{
}
/// <summary>
/// Get delimiter for data, defaults to ' unless specifed for data type
/// </summary>
protected string[] GetDelimiter(object value)
{
}
public class Criterion
{
public Criterion(string property, SqlComparison comparison, object value1)
{
Property = property;
Comparison = comparison;
Value1 = value1;
}
public Criterion(string property, SqlComparison comparison, object value1, object value2)
{
Property = property;
Comparison = comparison;
Value1 = value1;
Value2 = value2;
}
public Criterion(string property, SqlComparison comparison, object value1, bool not)
{
Property = property;
Comparison = comparison;
Value1 = value1;
Not = not;
}
public Criterion(string property, SqlComparison comparison, object value1, object value2, bool not)
{
Property = property;
Comparison = comparison;
Value1 = value1;
Value2 = value2;
Not = not;
}
public string Property { get; set; }
public bool Not { get; set; }
public SqlComparison Comparison { get; set; }
public object Value1 { get; set; }
public object Value2 { get; set; }
}
public enum SqlComparison { StartsWith, Contains, Equals, Between, MoreThan, LessThan, InList }
}
one last question please. I am using generic class to access sessionfactory so i cannot explicitly access the minisession. with this how do i access minisession based only on certain entities if the full session is not available and full session factory when it is available.
you never set sessionfactoryready to true
Update: a more complete example.
void Main()
{
Database.InitRealFactoryAsync("<sessionFactoryConfigPath>");
var minifactory = Database.GetMiniFactory("<sessionFactoryConfigPath>");
// Do some stuff with minifactory
var realsessionfactory = Database.SessionFactory;
// Do stuff with real factory
}
static class Database
{
private static ISessionFactory sessionFactory;
public void InitRealFactoryAsync(string sessionFactoryConfigPath)
{
ThreadPool.QueueUserWorkItem(state =>
{
sessionFactory = Fluently.Configure()
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load("nameofassembly"))
.Conventions.Add(DefaultLazy.Always(),
OptimisticLock.Is(x => x.All()),
DynamicUpdate.AlwaysTrue(),
DynamicInsert.AlwaysFalse(),
DefaultCascade.None())
.Conventions.AddFromAssemblyOf<FoxproDateConvention>())
.BuildSessionFactory();
});
}
public ISessionFactory GetMiniFactory(string sessionFactoryConfigPath)
{
var assembly = Assembly.Load("nameofassembly");
return Fluently.Configure()
.Mappings(m => m.FluentMappings.Add(assembly.GetTypes().Where(Filter).ToArray())
.Conventions.Add(DefaultLazy.Always(),
OptimisticLock.Is(x => x.All()),
DynamicUpdate.AlwaysTrue(),
DynamicInsert.AlwaysFalse(),
DefaultCascade.None())
.Conventions.AddFromAssemblyOf<FoxproDateConvention>())
.BuildSessionFactory();
}
public static ISessionFactory SessionFactory
{
get {
while (sessionFactory == null) Thread.Sleep(1000);
return sessionFactory;
}
}
}
UpdateUpdate:
void Main()
{
Database.InitRealFactoryAsync("<sessionFactoryConfigPath>");
Database.InitMiniFactory("<sessionFactoryConfigPath>");
using (var session = Database.GetSession(true))
{
// Do some stuff where minifactory is enough
}
using (var session = Database.GetSession())
{
// Do stuff with real factory
}
...
}
// class Database
public ISession GetSession()
{
return GetSession(false);
}
public ISession GetSession(bool miniFactoryIsEnough)
{
if (realSessionfactory != null)
return realSessionfactory.OpenSession();
if (miniFactoryIsEnough)
return miniSessionfactory.OpenSession();
else
{
while (realSessionFactory == null) Thread.Sleep(1000);
return realSessionfactory.OpenSession();
}
}
Update: "access minisession based only on certain entities"
you need to specify the type you want to use in the session:
public ISession GetSession(Type persistentType)
{
if (fullSessionfactory != null)
return realSessionfactory.OpenSession();
if (miniFactory.GetClassMetadata(persistentType) != null)
return miniSessionfactory.OpenSession();
else
{
// minifactory doesnt contain the type needed, wait for full factory
while (fullSessionFactory == null) Thread.Sleep(1000);
return fullSessionfactory.OpenSession();
}
}
some additional advice
do not catch (NHibernate.HibernateException)
you lose valuable information and the calling code can't really decide what to do when false is returned
session state is inconsistent see https://stackoverflow.com/a/1819150/671619
FlushMode should be Flushmode.Commit and public void CommitChanges() can be written as
var session = NHibernateSession;
if (session.Transaction.IsActiv)
{
session.Transaction.Commit();
}
cut out the whole sessionId stuff as it seems to provide no value. hold the session instead of sessionId instead
My code at the moment looks like this:
Server side:
#region IClientCallback interface
interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveWcfElement(WcfElement wcfElement);
}
#endregion
#region IService interface
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
interface IService
{
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void ReadyToReceive(string userName, int source, string ostatniTypWiadomosci);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
bool SendWcfElement(WcfElement wcfElement);
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
List<int> Login(Client name, string password, bool isAuto, bool isSuperMode);
}
#endregion
#region Public enums/event args
public delegate void WcfElementsReceivedFromClientEventHandler(object sender, WcfElementsReceivedFromClientEventArgs e);
public class WcfElementsReceivedFromClientEventArgs : EventArgs
{
public string UserName;
}
public class ServiceEventArgs : EventArgs
{
public WcfElement WcfElement;
public Client Person;
}
#endregion
#region Service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
#region Instance fields
//thread sync lock object
private static readonly Object SyncObj = new Object();
//callback interface for clients
IClientCallback _callback;
//delegate used for BroadcastEvent
public delegate void ChatEventHandler(object sender, ServiceEventArgs e);
public static event ChatEventHandler ChatEvent;
private ChatEventHandler _myEventHandler;
//holds a list of clients, and a delegate to allow the BroadcastEvent to work
//out which chatter delegate to invoke
static readonly Dictionary<Client, ChatEventHandler> Clients = new Dictionary<Client, ChatEventHandler>();
//current person
private Client _client;
#endregion
#region Helpers
private bool CheckIfPersonExists(string name)
{
return Clients.Keys.Any(p => p.UserName.Equals(name, StringComparison.OrdinalIgnoreCase));
}
private ChatEventHandler getPersonHandler(string name)
{
foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
ChatEventHandler chatTo;
Clients.TryGetValue(c, out chatTo);
return chatTo;
}
return null;
}
private Client GetPerson(string name)
{
return Clients.Keys.FirstOrDefault(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase));
}
#endregion
#region IService implementation
public List<int> Login(Client client, string password, bool isAuto, bool isSuperMode)
{
if (client.ElementsVersions == null)
{
client.ElementsVersions = new WcfElement(WcfElement.RodzajWiadomosci.VersionControl, client.UserName);
}
//create a new ChatEventHandler delegate, pointing to the MyEventHandler() method
_myEventHandler = MyEventHandler;
lock (SyncObj)
{
if (!CheckIfPersonExists(client.UserName))
{
_client = client;
Clients.Add(client, _myEventHandler);
}
else
{
_client = client;
foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(client.UserName)))
{
ChatEvent -= Clients[c];
Clients.Remove(c);
break;
}
Clients[client] = _myEventHandler;
}
_client.LockObj = new object();
}
_callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
ChatEvent += _myEventHandler;
var rValue = isAuto ? bazaDanych.Login(client.UserName, isSuperMode) : bazaDanych.Login(client.UserName, password);
return rValue;
}
public void PerformDataSync(Client c)
{
WcfElement wcfDelete = null;
WcfElement wcfUpdate = null;
//...
//this method prepares elements for client
//when done it adds them to clients queue (List<WcfElement)
try
{
var counter = 0;
if (wcfDelete != null)
{
foreach (var wcf in WcfElement.SplitWcfElement(wcfDelete, false))//split message into small ones
{
c.AddElementToQueue(wcf, counter++);
}
}
if (wcfUpdate != null)
{
foreach (var wcf in WcfElement.SplitWcfElement(wcfUpdate, true))
{
c.AddElementToQueue(wcf, counter++);
}
}
SendMessageToGui(string.Format("Wstępna synchronizacja użytkownika {0} zakończona.", c.UserName));
c.IsSynchronized = true;
}
catch (Exception e)
{
}
}
private void SendMessageToClient(object sender, EventArgs e)
{
var c = (Client) sender;
if (c.IsReceiving || c.IsSending)
{
return;
}
c.IsReceiving = true;
var wcfElement = c.GetFirstElementFromQueue();
if (wcfElement == null)
{
c.IsReceiving = false;
return;
}
Clients[c].Invoke(this, new ServiceEventArgs { Person = c, WcfElement = wcfElement });
}
public void ReadyToReceive(string userName)
{
var c = GetPerson(userName);
c.IsSending = false;
c.IsReceiving = false;
if (c.IsSynchronized)
{
SendMessageToClient(c, null);
}
else
{
PerformDataSync(c);
}
}
public bool SendWcfElement(WcfElement wcfElement)
{
var cl = GetPerson(wcfElement.UserName);
cl.IsSending = true;
if (wcfElement.WcfElementVersion != bazaDanych.WcfElementVersion) return false;
//method processes messages and if needed creates creates WcfElements which are added to every clients queue
return ifSuccess;
}
#endregion
#region private methods
private void MyEventHandler(object sender, ServiceEventArgs e)
{
try
{
_callback.ReceiveWcfElement(e.WcfElement);
}
catch (Exception ex)
{
}
}
#endregion
}
#endregion
Client side in a moment
#region Client class
[DataContract]
public class Client
{
#region Instance Fields
/// <summary>
/// The UserName
/// </summary>
[DataMember]
public string UserName { get; set; }
[DataMember]
public WcfElement ElementsVersions { get; set; }
private bool _isSynchronized;
public bool IsSynchronized
{
get { return _isSynchronized; }
set
{
_isSynchronized = value;
}
}
public bool IsSending { get; set; }
public bool IsReceiving { get; set; }
private List<WcfElement> ElementsQueue { get; set; }
public object LockObj { get; set; }
public void AddElementToQueue(WcfElement wcfElement, int position = -1)
{
try
{
lock (LockObj)
{
if (ElementsQueue == null) ElementsQueue = new List<WcfElement>();
if (position != -1 && position <= ElementsQueue.Count)
{
try
{
ElementsQueue.Insert(position, wcfElement);
}
catch (Exception e)
{
}
}
else
{
try
{
//dodaje na koncu
ElementsQueue.Add(wcfElement);
}
catch (Exception e)
{
}
}
}
}
catch (Exception e)
{
}
}
public WcfElement GetFirstElementFromQueue()
{
if (ElementsQueue == null) return null;
if (ElementsQueue.Count > 0)
{
var tmp = ElementsQueue[0];
ElementsQueue.RemoveAt(0);
return tmp;
}
return null;
}
#endregion
#region Ctors
/// <summary>
/// Assign constructor
/// </summary>
/// <param name="userName">The userName to use for this client</param>
public Client(string userName)
{
UserName = userName;
}
#endregion
}
#endregion
ProxySingletion:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public sealed class ProxySingleton : IClientCallback
{
#region Instance Fields
private static ProxySingleton _singleton;
public static bool IsConnected;
private static readonly object SingletonLock = new object();
private ServiceProxy _proxy;
private Client _myPerson;
private delegate void HandleDelegate(Client[] list);
private delegate void HandleErrorDelegate();
//main proxy event
public delegate void ProxyEventHandler(object sender, ProxyEventArgs e);
public static event ProxyEventHandler ProxyEvent;
//callback proxy event
public delegate void ProxyCallBackEventHandler(object sender, ProxyCallBackEventArgs e);
public static event ProxyCallBackEventHandler ProxyCallBackEvent;
#endregion
#region Ctor
/// <summary>
/// Blank constructor
/// </summary>
private ProxySingleton()
{
}
#endregion
#region Public Methods
#region IClientCallback implementation
public void ReceiveWcfElement(WcfElement wcfElement)
{
//process received data
//...
ReadyToReceive();
}
#endregion
public void ReadyToReceive()
{
try
{
if (bazaDanych.Dane.Client.IsSending) return;
var w = bazaDanych.Dane.Client.GetFirstElementFromQueue();
if (w != null)
{
SendWcfElement(w);
return;
}
_proxy.ReadyToReceive(bazaDanych.Dane.Client.UserName, source, ostatniTypWiadomosci);
}
catch (Exception)
{
IsConnected = false;
}
}
public static WcfElement CurrentWcfElement;
public bool SendWcfElement(WcfElement wcfElement)
{
if (bazaDanych.Dane.Client.IsReceiving)
{
bazaDanych.Dane.Client.AddElementToQueue(wcfElement);
return true;
}
bazaDanych.Dane.Client.IsSending = true;
foreach (var wcfElementSplited in WcfElement.SplitWcfElement(wcfElement, true))
{
CurrentWcfElement = wcfElementSplited;
try
{
var r = _proxy.SendWcfElement(wcfElementSplited);
CurrentWcfElement = null;
}
catch (Exception e)
{
IsConnected = false;
return false;
}
}
bazaDanych.Dane.Client.IsSending = false;
ReadyToReceive();
return true;
}
public void ListenForConnectOrReconnect(EventArgs e)
{
SendWcfElement(WcfElement.GetVersionElement());//send wcfelement for perform PerformDataSync
ReadyToReceive();
}
public static bool IsReconnecting;
public bool ConnectOrReconnect(bool shouldRaiseEvent = true)
{
if (IsReconnecting)
{
return IsConnected;
}
if (IsConnected) return true;
IsReconnecting = true;
bazaDanych.Dane.Client.IsReceiving = false;
bazaDanych.Dane.Client.IsSending = false;
bazaDanych.Dane.Client.IsSynchronized = false;
try
{
var site = new InstanceContext(this);
_proxy = new ServiceProxy(site);
var list = _proxy.Login(bazaDanych.Dane.Client, bazaDanych.Dane.UserPassword, bazaDanych.Dane.UserIsAuto, bazaDanych.Dane.UserIsSuperMode);
bazaDanych.Dane.UserRights.Clear();
bazaDanych.Dane.UserRights.AddRange(list);
IsConnected = true;
if (shouldRaiseEvent) ConnectOrReconnectEvent(null);
}
catch (Exception e)
{
IsConnected = false;
}
IsReconnecting = false;
return IsConnected;
}
}
#endregion
At the moment my app works like this:
After successful login every client sends WcfElements(which contains bunch of list with ids and versions of elements). Then it sends ReadyToReceive one way message which after login fires performsync method. That method prepares data for client and sends first of them using one way receive method. IF there is more than one wcfelement to send then only last one is marked as last. Client responds with ReadyToReceive after every successful receive from Server. All up to this point works quite well. Problem starts later. Mostly packages are lost (method receiveWcfElement). Server has marked that client is receiving and maybe processing message and is waitng for readytoreceive packet, which will never be send because of lost element.
I've made it like this because as far as I know client can't send and receive at the same time. I've tried this and got this problem:
If client send wcfElement with SendWcfElement method and server due to processing this element created another element which was supposed to be ssend back to client then client whoud have faulted proxy if callback was send before sendWcfElement returned true indicating that method was completed.
Now I wonder if it is possible for client to send and receive at the same time using two way methods ?
I ended up with to services(two connections). One for connection from client to server and another with callback which handles connection from server to client.
How can I get Ninject.Extensions.Interception to basically let me bind a specific interceptor to any method that has an attribute... psudocode:
Kernel.Intercept(context => context.Binding.HasAttribute<TransactionAttribute>())
.With<TransactionInterceptor>
With a class like:
public SomeClass
{
[TransactionAttribute]
public void SomeTransactedMethod()
{ /*do stuff */ }
}
Assuming that you are using Ninject.Extensions.Interception this should do the trick
public class TransactionInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// Do something...
}
}
public class TransactionAttribute : InterceptAttribute
{
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return new TransactionInterceptor();
}
}
public class SomeClass
{
[Transaction]
public virtual void SomeTransactedMethod() { }
}
Make sure that the method that should be intercepted is marked as virtual.
When SomeTransactedMethod() is called it should be intercepted.
var kernel = new StandardKernel();
kernel.Bind<SomeClass>().ToSelf();
var someClass = kernel.Get<SomeClass>();
someClass.SomeTransactedMethod();
UPDATE
You could create a custom planning strategy.
public class CustomPlanningStrategy<TAttribute, TInterceptor> :
NinjectComponent, IPlanningStrategy
where TAttribute : Attribute
where TInterceptor : IInterceptor
{
private readonly IAdviceFactory adviceFactory;
private readonly IAdviceRegistry adviceRegistry;
public CustomPlanningStrategy(
IAdviceFactory adviceFactory, IAdviceRegistry adviceRegistry)
{
this.adviceFactory = adviceFactory;
this.adviceRegistry = adviceRegistry;
}
public void Execute(IPlan plan)
{
var methods = GetCandidateMethods(plan.Type);
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(
typeof(TAttribute), true) as TAttribute[];
if (attributes.Length == 0)
{
continue;
}
var advice = adviceFactory.Create(method);
advice.Callback = request => request.Kernel.Get<TInterceptor>();
adviceRegistry.Register(advice);
if (!plan.Has<ProxyDirective>())
{
plan.Add(new ProxyDirective());
}
}
}
}
private static IEnumerable<MethodInfo> GetCandidateMethods(Type type)
{
var methods = type.GetMethods(
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance
);
return methods.Where(ShouldIntercept);
}
private static bool ShouldIntercept(MethodInfo methodInfo)
{
return methodInfo.DeclaringType != typeof(object) &&
!methodInfo.IsPrivate &&
!methodInfo.IsFinal;
}
}
This should now work.
var kernel = new StandardKernel();
kernel.Components.Add<IPlanningStrategy,
CustomPlanningStrategy<TransactionAttribute, TransactionInterceptor>>();
kernel.Bind<SomeClass>().ToSelf();
var someClass = kernel.Get<SomeClass>();
someClass.SomeTransactedMethod();
Here is the code that I used for the same purpose
//Code in Bind Module
this.Bind(typeof(ServiceBase<,>))
.ToSelf()
.InRequestScope()
.Intercept()
.With<TransactionInterceptor>();
And
public class TransactionInterceptor : IInterceptor
{
#region Constants and Fields
public ISession session;
private ISessionFactory sessionFactory;
#endregion
#region Constructors and Destructors
public TransactionInterceptor(ISession session, ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
this.session = session;
}
#endregion
public void Intercept(IInvocation invocation)
{
try
{
if (!session.IsConnected)
session = sessionFactory.OpenSession();
session.BeginTransaction();
invocation.Proceed();
if (this.session == null)
{
return;
}
if (!this.session.Transaction.IsActive)
{
return;
}
else
{
this.session.Transaction.Commit();
}
}
catch (Exception)
{
if (this.session == null)
{
return;
}
if (!this.session.Transaction.IsActive)
{
return;
}
this.session.Transaction.Rollback();
throw;
}
}
}
And code for TransactionAttribute
public class TransactionAttribute : InterceptAttribute
{
#region Public Methods
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return request.Context.Kernel.Get<TransactionInterceptor>() ;
}
#endregion
}
EDIT
Ok, I've got it, it's not serializable.........so, how do I go about serializing it?
Scenario
I have a custom BindableDictionary
that I use to bind with a grid
control for automatically updating
the grid when the underlying
datasource changes.
I now want to extend my application
to use WCF, so that when the
server-side has finished updating
the BindableDictionary with new
values, it can pass this dictionary
to the client, which in turn can
perform the update on the Client
GUI.
Problem
How do I go about sending this custom
BindableDictionary over the wire?
What is the best way to implement this?
The reason I ask about an implementation method is that every example I've seen of Duplex, Callbacks or the Observer Pattern for "PUSHING" my data to the client, only uses an example of passing strings, and it's very primitive - Whenever I update the examples to use my custom BindableDictionary, I cannot get it to work.
Am I missing something?
BindableDictionary
public class BindableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IBindingList
{
private Dictionary<TKey, TValue> source = new Dictionary<TKey, TValue>();
void IBindingList.AddIndex(PropertyDescriptor property) { }
object IBindingList.AddNew() { throw new NotImplementedException(); }
bool IBindingList.AllowEdit { get { return false; } }
bool IBindingList.AllowNew { get { return false; } }
bool IBindingList.AllowRemove { get { return false; } }
void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) { }
int IBindingList.Find(PropertyDescriptor property, object key) { throw new NotImplementedException(); }
bool IBindingList.IsSorted { get { return false; } }
void IBindingList.RemoveIndex(PropertyDescriptor property) { }
void IBindingList.RemoveSort() { }
ListSortDirection IBindingList.SortDirection { get { return ListSortDirection.Ascending; } }
PropertyDescriptor IBindingList.SortProperty { get { return null; } }
bool IBindingList.SupportsChangeNotification { get { return true; } }
bool IBindingList.SupportsSearching { get { return false; } }
bool IBindingList.SupportsSorting { get { return false; } }
int System.Collections.IList.Add(object value) { throw new NotImplementedException(); }
void System.Collections.IList.Clear() { Clear(); }
bool System.Collections.IList.Contains(object value) { if (value is TKey) { return source.ContainsKey((TKey)value); } else if (value is TValue) { return source.ContainsValue((TValue)value); } return false; }
int System.Collections.IList.IndexOf(object value) { return -1; }
void System.Collections.IList.Insert(int index, object value) { throw new NotImplementedException(); }
bool System.Collections.IList.IsFixedSize { get { return false; } }
bool System.Collections.IList.IsReadOnly { get { return true; } }
void System.Collections.IList.Remove(object value) { if (value is TKey) { Remove((TKey)value); } }
void System.Collections.IList.RemoveAt(int index) { throw new NotImplementedException(); }
object System.Collections.IList.this[int index] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
private ListChangedEventHandler listChanged;
event ListChangedEventHandler IBindingList.ListChanged
{
add { listChanged += value; }
remove { listChanged -= value; }
}
protected virtual void OnListChanged(ListChangedEventArgs e)
{
var evt = listChanged;
if (evt != null) evt(this, e);
}
public void Add(TKey key, TValue value)
{
source.Add(key, value);
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
public bool Remove(TKey key)
{
if (source.Remove(key))
{
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
return true;
}
return false;
}
public TValue this[TKey key]
{
get
{
return source[key];
}
set
{
source[key] = value;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
((ICollection<KeyValuePair<TKey, TValue>>)source).Add(item);
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
if (((ICollection<KeyValuePair<TKey, TValue>>)source).Remove(item))
{
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
return true;
}
return false;
}
public bool ContainsKey(TKey key) { return source.ContainsKey(key); }
public ICollection<TKey> Keys { get { return source.Keys; } }
public bool TryGetValue(TKey key, out TValue value) { return source.TryGetValue(key, out value); }
public ICollection<TValue> Values { get { return source.Values; } }
public void Clear() { source.Clear(); }
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { return ((ICollection<KeyValuePair<TKey, TValue>>)source).Contains(item); }
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { ((ICollection<KeyValuePair<TKey, TValue>>)source).CopyTo(array, arrayIndex); }
public int Count { get { return source.Count; } }
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { get { return ((ICollection<KeyValuePair<TKey, TValue>>)source).IsReadOnly; } }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return source.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return null; } }
void ICollection.CopyTo(Array array, int arrayIndex) { ((ICollection)source).CopyTo(array, arrayIndex); }
}
If you have a specific set of key/values for your BindableDictionary, you could set up your own "data-transfer" object that reflects those values:
[DataContract]
public class MyDTOType
{
[DataMember]
public (keytype) Key { get; set; }
[DataMember]
public (valuetype) Value { get; set; }
}
and then have a DTO object that contains a List<MyDTOType>:
[DataContract]
public class MyDTOList
{
[DataMember]
public List<MyDTOType> ListOfKeyValues { get; set; }
}
Now, as long as you have serializable types for the (keytype) and the (valuetype) in your MyDTOType class, this will be able to travel across the wire in WCF:
[ServiceContract]
interface IMyService
{
[OperationContract]
public MyDTOList GetAllValues(int someCriteria);
}
and the last step you need to find a way to handle nicely is converting your BindableDictionary<TKey, TValue) to a MyDTOList that contains a list of MyDTOType instances that contain a key of TKey type and a value of TValue type. You could probably use something like AutoMapper to handle this conversion on an item-by-item basis.
The answer was that the dictionary was not serializable.
I have designed a serializable dictionary now that works.