Get serialization error when try and submit EF4 entity via WCF - wcf

I have an entity CustomerActivityReport which I'm trying to submit to the server via WCF. On the server end I'm using the repository + UOW patterns to update/insert the entity into the db.
CustomerActivityReport has a many to many relationship to another entity LookupValue. When I try and submit an instance of CustomerActivityReport, the DataContractSerializer throws the error: "Object graph for type 'FixupCollection[CustomerActivityReport]' contains cycles and cannot be serialized if reference tracking is disabled". I am getting this error even when I don't set the relationship on the LookupValue entities.
To get around this I've tried applying [DataContract(IsReference = true)] to both the entities in question and also to FixupCollection. But then I get different problems.
Has anybody else run into similar problems when trying to submit related entities over WCF?
Thanks in advance for any replies.
Ryan

The times that we have had a similar problem we were missing an attribute on a sub object.

I couldn't get this working with FixupCollection, and so I've had to submit all my entity collections as standard Collection, and then add logic server end to change them back into FixupCollection.
Client:
convertedCustomerActivityReport.LookupValues = new Collection<LookupValue>()
Server:
public virtual ICollection<LookupValue> LookupValues
{
get
{
if (_lookupValues == null || _lookupValues is Array)
{
var newCollection = new FixupCollection<LookupValue>();
newCollection.CollectionChanged += FixupLookupValues;
newCollection.AddRange(_lookupValues);
_lookupValues = newCollection;
}
return _lookupValues;
}
I've also added an AddRange method to FixupCollection:
/// <summary>
/// Adds multiple items.
/// </summary>
/// <param name="items">The items to add.</param>
public void AddRange(IEnumerable<T> items)
{
if (items == null)
{
return;
}
foreach (var item in items)
{
this.Add(item);
}
}

Related

How do I add a lazy loaded column in EntitySpaces?

If you do not have experience with or aren't currently using EntitySpaces ("ES") ORM this question is not meant for you.
I have a 10 year old application that after 4 years now needs my attention. My application uses a now defunct ORM called EntitySpaces and I'm hoping if you're reading this you have experience or maybe still use it too! Switching to another ORM is not an option at this time so I need to find a way to make this work.
Between the time I last actively worked on my application and now (ES Version 2012-09-30), EntitySpaces ("ES") has gone through a significant change in the underlying ADO.net back-end. The scenario that I'm seeking help on is when an entity collection is loaded with only a subset of the columns:
_products = new ProductCollection();
_products.Query.SelectAllExcept(_products.Query.ImageData);
_products.LoadAll();
I then override the properties that weren't loaded in the initial select so that I may lazyload them in the accessor. Here is an example of one such lazy-loaded property that used to work perfectly.
public override byte[] ImageData
{
get
{
bool rowIsDirty = base.es.RowState != DataRowState.Unchanged;
// Check if we have loaded the blob data
if(base.Row.Table != null && base.Row.Table.Columns.Contains(ProductMetadata.ColumnNames.ImageData) == false)
{
// add the column before we can save data to the entity
this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
}
if(base.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull)
{
// Need to load the data
Product product = new Product();
product.Query.Select(product.Query.ImageData).Where(product.Query.ProductID == base.ProductID);
if(product.Query.Load())
{
if (product.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull == false)
{
base.ImageData = product.ImageData;
if (rowIsDirty == false)
{
base.AcceptChanges();
}
}
}
}
return base.ImageData;
}
set
{
base.ImageData = value;
}
}
The interesting part is where I add the column to the underlying DataTable DataColumn collection:
this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
I had to comment out all the ADO.net related stuff from that accessor when I updated to the current (and open source) edition of ES (version 2012-09-30). That means that the "ImageData" column isn't properly configured and when I change it's data and attempt to save the entity I receive the following error:
Column 'ImageData' does not belong to table .
I've spent a few days looking through the ES source and experimenting and it appears that they no longer use a DataTable to back the entities, but instead are using a 'esSmartDictionary'.
My question is: Is there a known, supported way to accomplish the same lazy loaded behavior that used to work in the new version of ES? Where I can update a property (i.e. column) that wasn't included in the initial select by telling the ORM to add it to the entity backing store?
After analyzing how ES constructs the DataTable that is uses for updates it became clear that columns not included in the initial select (i.e. load) operation needed to be added to the esEntityCollectionBase.SelectedColumns dictionary. I added the following method to handle this.
/// <summary>
/// Appends the specified column to the SelectedColumns dictionary. The selected columns collection is
/// important as it serves as the basis for DataTable creation when updating an entity collection. If you've
/// lazy loaded a column (i.e. it wasn't included in the initial select) it will not be automatically
/// included in the selected columns collection. If you want to update the collection including the lazy
/// loaded column you need to use this method to add the column to the Select Columns list.
/// </summary>
/// <param name="columnName">The lazy loaded column name. Note: Use the {yourentityname}Metadata.ColumnNames
/// class to access the column names.</param>
public void AddLazyLoadedColumn(string columnName)
{
if(this.selectedColumns == null)
{
throw new Exception(
"You can only append a lazy-loaded Column to a partially selected entity collection");
}
if (this.selectedColumns.ContainsKey(columnName))
{
return;
}
else
{
// Using the count because I can't determine what the value is supposed to be or how it's used. From
// I can tell it's just the number of the column as it was selected: if 8 colums were selected the
// value would be 1 through 8 - ??
int columnValue = selectedColumns.Count;
this.selectedColumns.Add(columnName, columnValue);
}
}
You would use this method like this:
public override System.Byte[] ImageData
{
get
{
var collection = this.GetCollection();
if(collection != null)
{
collection.AddLazyLoadedColumn(ProductMetadata.ColumnNames.ImageData);
}
...
It's a shame that nobody is interested in the open source EntitySpaces. I'd be happy to work on it if I thought it had a future, but it doesn't appear so. :(
I'm still interested in any other approaches or insight from other users.

Returning the class which is a foreign key in the database

I want to ask this question and I tried to search for a while without concrete answers.
I have made a database and used LINQ2SQL to auto-generate the classes needed.
I have set the serialization mode to unidirectional to make sure the classes are being serialized and making the datamembers.
Now, what I want to know is, how I can send the references to the other classes (which has been made through LINQ2SQL).
F.x. I have a Class called Scheduler which is referencing Reservation, and Seat, because Reservation and Seat have foreign keys.
You can see the dbml here:
http://imgur.com/rR6OxDi
The dbml file. This is the model of our database
Also you can see that when I run the WCF test client it does not return the objects of Seats and Reservation.
http://imgur.com/brxNBz7
Hopefully you can all help.
UPDATE
Here is the snippet of the code provided by LINQ2SQL.
This is the fields for the scheduler
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Scheduler")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Scheduler : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _SchID;
private System.Nullable<System.DateTime> _Date;
private System.Nullable<System.TimeSpan> _Starttime;
private System.Nullable<int> _MovieID;
private System.Nullable<int> _HallID;
private EntitySet<Seat> _Seats;
private EntitySet<Reservation> _Reservations;
private EntityRef<Hall> _Hall;
private EntityRef<Movie> _Movie;
private bool serializing;
And here is the snippet part of the code where it references to Reservation and Seat:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Seat", Storage="_Seats", ThisKey="SchID", OtherKey="SchedulerID")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=6, EmitDefaultValue=false)]
public EntitySet<Seat> Seats
{
get
{
if ((this.serializing
&& (this._Seats.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Seats;
}
set
{
this._Seats.Assign(value);
}
}
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Reservation", Storage="_Reservations", ThisKey="SchID", OtherKey="SchedulerID")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=7, EmitDefaultValue=false)]
public EntitySet<Reservation> Reservations
{
get
{
if ((this.serializing
&& (this._Reservations.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Reservations;
}
set
{
this._Reservations.Assign(value);
}
}
Update 2
Here is the Reservation class which LINQ2SQL made:
Here is the fields:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Reservation")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Reservation : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _ResID;
private System.Nullable<int> _CustomerID;
private System.Nullable<int> _SchedulerID;
private string _Row;
private string _Seat;
private EntityRef<Customer> _Customer;
private EntityRef<Scheduler> _Scheduler;
And here is the Scheduler reference part of the class
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Reservation", Storage="_Scheduler", ThisKey="SchedulerID", OtherKey="SchID", IsForeignKey=true, DeleteRule="SET DEFAULT")]
public Scheduler Scheduler
{
get
{
return this._Scheduler.Entity;
}
set
{
Scheduler previousValue = this._Scheduler.Entity;
if (((previousValue != value)
|| (this._Scheduler.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Scheduler.Entity = null;
previousValue.Reservations.Remove(this);
}
this._Scheduler.Entity = value;
if ((value != null))
{
value.Reservations.Add(this);
this._SchedulerID = value.SchID;
}
else
{
this._SchedulerID = default(Nullable<int>);
}
this.SendPropertyChanged("Scheduler");
}
}
}
All of these things should lead to where I could get the object like this:
Scheduler[] schedulers = client.GetAllSchedulers();
Reservation reservation = schedulers[0].Reservations.First();
But get this error due to WCF not sending the object, (which you could see in picture one).
Which is this error:
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Sequence contains
no elements
UPDATE 3:
Ok so it appears that it works somehow.
I just had to make a join between the Scheduler and Reservation.
Also whenever I debug the code I can see the variables are there. (Due to my reputation I can not post links).
But some of you might recognize the following whenever you try to view a result in debug mode:
"expanding the results view will enumerate the ienumerable c#"
Whenever I do this, it works, but not if I run it in release mode.
Looks like only object types (Reservation,Seat) have null values.
I'm guessing either you are missing DataContract/DataMember attributes in your complex types or you might need to include KnownTypeAttribute
It'd be easier to tell if you could provide some code.
EDIT
What your are talking about later is deferred loading. See this blog for more information on deferred vs immediate loading.
When you expand the IEnumerable in debug mode, that makes the request to retrieve/load the objects.
What your probably want is to load your Reservation,Seat objects along with your Scheduler object. Something like the following:
YourDatabaseContext database = new YourDatabaseContext ())
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Scheduler>(sch=> sch.Reservation);
options.LoadWith<Scheduler>(sch=> sch.Seat);
database.LoadOptions = options;
}
See DataLoadOptions for more details.
If you want to understand deferred execution. See this article for more details.
Quote from the article:
By default LINQ uses deferred query execution. This means when you write a LINQ query it doesn't execute. LINQ queries execute when you 'touch' the query results. This means you can change the underlying collection and run the same query subsequent times in the same scope. Touching the data means accessing the results, for instance in a for loop or by using an aggregate operator like Average or AsParallel on the results.

nhibernate session with custom data and event listeners

For a WCF project I'm working on, I need to implement some kind of audit log for the entities we're persisting. Basically the audit trail will consist of 4 mandatory fields
CreatedDateTime
CreatedUserID
UpdatedDateTime
UpdatedUserID
I'm trying to implement this through nHibernate Event Listeners in my DataAccess layer as I think this should not be in the domain layer. So far, I've got the DateTime stuff working as expected, but haven't been able to figure out how to retrieve the userID in the event listener. Ideally, I would like to have the user id as some sort of custom data attached to the nHibernate session object.
Any suggestion is greatly appreciated.
The .Net framework already has builtin support for contextual user identity information: Thread.CurrentPrincipal http://msdn.microsoft.com/en-us/library/system.threading.thread.currentprincipal.aspx
Use one of the available IPrincipal implementations or create your own - it's easy. Then set the property early on, in some "begin-request" method.
There is also HttpContext.User to be aware of in ASP.NET code.
Here's how I'm doing it but I don't have experience doing this with WCF. Note that this requires a reference to System.Web.
/// <summary>
/// Returns the user name of the current user. Gets user name from HttpContext if running as a web app, else WindowsIdentity.
/// </summary>
private static string GetUserName()
{
var identity = HttpContext.Current == null ? WindowsIdentity.GetCurrent() : HttpContext.Current.User.Identity;
if (identity == null)
{
throw new Exception("Identity could not be determined.");
}
// Remove domain name if present
var s = identity.Name;
var stop = s.IndexOf("\\", StringComparison.InvariantCultureIgnoreCase);
return (stop > -1) ? s.Substring(stop + 1, s.Length - stop - 1).ToUpper() : s;
}

Does NHibernate really deliver transparent persistency

Starting to use Nhibernate for persistency being seduced by the promise that it respects your domain model, I tried to implement a relation manager for my domain objects. Basically, to DRY my code with respect to managing bidirectional one to many and many to many relations, I decided to have those relations managed by a separate class. When a one to many or many to one property is set an entry for the two objects is made in an dictionary, the key is either a one side with a collection value to hold the many sides, or a many side with a value of the one side.
A one to many relation for a specific combination of types looks as follows:
public class OneToManyRelation<TOnePart, TManyPart> : IRelation<IRelationPart, IRelationPart>
where TOnePart : class, IRelationPart
where TManyPart : class, IRelationPart
{
private readonly IDictionary<TOnePart, Iesi.Collections.Generic.ISet<TManyPart>> _oneToMany;
private readonly IDictionary<TManyPart, TOnePart> _manyToOne;
public OneToManyRelation()
{
_manyToOne = new ConcurrentDictionary<TManyPart, TOnePart>();
_oneToMany = new ConcurrentDictionary<TOnePart, Iesi.Collections.Generic.ISet<TManyPart>>();
}
public void Set(TOnePart onePart, TManyPart manyPart)
{
if (onePart == null || manyPart == null) return;
if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
else _manyToOne[manyPart] = onePart;
}
public void Add(TOnePart onePart, TManyPart manyPart)
{
if (onePart == null || manyPart == null) return;
if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
else _manyToOne[manyPart] = onePart;
if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, new HashedSet<TManyPart>());
_oneToMany[onePart].Add(manyPart);
}
public Iesi.Collections.Generic.ISet<TManyPart> GetManyPart(TOnePart onePart)
{
if (!_oneToMany.ContainsKey(onePart)) _oneToMany[onePart] = new HashedSet<TManyPart>();
return _oneToMany[onePart];
}
public TOnePart GetOnePart(TManyPart manyPart)
{
if(!_manyToOne.ContainsKey(manyPart)) _manyToOne[manyPart] = default(TOnePart);
return _manyToOne[manyPart];
}
public void Remove(TOnePart onePart, TManyPart manyPart)
{
_manyToOne.Remove(manyPart);
_oneToMany[onePart].Remove(manyPart);
}
public void Set(TOnePart onePart, Iesi.Collections.Generic.ISet<TManyPart> manyPart)
{
if (onePart == null) return;
if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, manyPart);
else _oneToMany[onePart] = manyPart;
}
public void Clear(TOnePart onePart)
{
var list = new HashedSet<TManyPart>(_oneToMany[onePart]);
foreach (var manyPart in list)
{
_manyToOne.Remove(manyPart);
}
_oneToMany.Remove(onePart);
}
public void Clear(TManyPart manyPart)
{
if (!_manyToOne.ContainsKey(manyPart)) return;
if (_manyToOne[manyPart] == null) return;
_oneToMany[_manyToOne[manyPart]].Remove(manyPart);
_manyToOne.Remove(manyPart);
}
}
On the many side a code snippet looks like:
public virtual SubstanceGroup SubstanceGroup
{
get { return RelationProvider.SubstanceGroupSubstance.GetOnePart(this); }
protected set { RelationProvider.SubstanceGroupSubstance.Set(value, this); }
}
On the one side, so, in this case the SubstanceGroup, the snippet looks like:
public virtual ISet<Substance> Substances
{
get { return RelationProvider.SubstanceGroupSubstance.GetManyPart(this); }
protected set { RelationProvider.SubstanceGroupSubstance.Set(this, value); }
}
Just using my domain objects, this works excellent. In the domain object I just have to reference an abstract factory that retrieves the appropriate relation and I can set the relation from one side, wich automatically becomes thus bidirectional.
However, when NH kicks in the problem is that I get duplicate keys in my dictionaries. Somehow NH sets a relation property with a null value(!) with a new copy(?) of a domain object. So when the domain object gets saved, I have two entries of that domain object in, for example the many side of the relation, i.e. _manyToOne dictionary.
This problem makes me lose my hair, I do not get it what is happening??
To answer your first, very general question: "Does NHibernate really deliver transparent persistency", I just can say: nothing is perfect. NH tries its best to be as transparent as possible, by also trying to keep its complexity as low as possible.
There are some assumptions, particularly regarding collections: Collections and their implementations are not considered to be part of your domain model. NH provides its own collection implementations. You are not only expected to use the interfaces like ISet and IList. You should also take the instances given by NH when the object is read from the database and never replace it with your own. (I don't know what your relation class is actually used for, so I don't know if this is the problem here.)
Domain objects are unique within the same instance of the session. If you get new instances of domain objects each time, you probably implemented the "session-per-call" anti-pattern, which creates a new session for each database interaction.
I don't have a clue what you actually are doing. How is this OneToManyRelation actually used for? What are you doing when NH doesn't behave as expected? This is a very specific problem to your specific implementation.
Besides the comments on 'convoluted code' and 'what the heck are you doing'. The problem was that I was replacing the persistence collections of NH like in the below code snippet:
public void Add(TOnePart onePart, TManyPart manyPart)
{
if (onePart == null || manyPart == null) return;
if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
else _manyToOne[manyPart] = onePart;
if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, new HashedSet<TManyPart>());
_oneToMany[onePart].Add(manyPart);
}
I create a new Hashed set for the many part. And that was the problem. If just has set the many part with the collection coming in (in case of the persistence collection implementation of NH) than it would have worked.
As a NH newbie, this replacing of collections with a special implementation from NH has been an important source of errors. Just as a warning to other NH newbies.

How to add a new entity to a domain context and immediately see it in data bound controls before SubmitChanges?

I've got a Silverlight 4 RIA Services (SP1) app using Entity Frameworks 4 CTP5. I can databind a grid or listbox to the IEnumerable loaded by the domain context and it shows data from the server. Great.
Now I want to create a new instance of MyEntity and add it to the client-side data so that the user can see the newly added entity. MyEntity is a true entity descendant, not a POCO.
The only Add method I can find is domainContext.EntityContainer.GetEntitySet<MyEntity>().Add(newobj)
This does add the new entity to the domain context, and the domainContext.HasChanges does become true, but the new entity doesn't show up in the databound controls.
How do I get the new entity to show up in the databound controls prior to SubmitChanges?
(Probably related to this SO question from years ago that never got an answer)
Here's the server side declarations of the domain service, per requests:
[EnableClientAccess()]
public class MyDomainService : LinqToEntitiesDomainService<MyObjectContext>
{
protected override MyObjectContext CreateObjectContext()
{
return new MyObjectContext();
}
public IQueryable<MyEntity> GetMyEntities()
{
return this.ObjectContext.MyEntities;
}
public void InsertMyEntity(MyEntity MyEntity)
{
// ...
}
public void UpdateMyEntity(MyEntity currentMyEntity)
{
// ...
}
public void DeleteMyEntity(MyEntity MyEntity)
{
// ...
}
}
I've figured this out with a combination of my own trial and error and hints provided by some of the other responses to this question.
The key point I was missing was that it's not enough for the ViewModel to keep track of the DomainContext and hand out query results to the View for databinding. The ViewModel also has to capture and retain the query results if you want entity adds and deletes performed by the ViewModel to appear in the UI before DomainContext.SubmitChanges(). The ViewModel has to apply those adds to the collection view of the query results.
The ViewModel collection property for View databinding. In this case I'm using the Telerik QueryableDomainServiceCollectionView, but other collection views can be used:
public IEnumerable<MyEntity> MyEntities
{
get
{
if (this.view == null)
{
DomainContextNeeded();
}
return this.view;
}
}
private void DomainContextNeeded()
{
this.context = new MyDomainContext();
var q = context.GetMyEntitiesQuery();
this.view = new Telerik.Windows.Data.QueryableDomainServiceCollectionView<MyEntity>(context, q);
this.view.Load();
}
The ViewModel function that adds a new entity for the UI to display:
public void AddNewMyEntity(object selectedNode)
{
var ent = new MyEntity() { DisplayName = "New Entity" };
if (selectedNode == null)
{
this.view.AddNew(ent);
}
else if (selectedNode is MyEntity)
{
((MyEntity)selectedNode).Children.Add(ent);
}
}
Other responses mentioned ObservableCollection. The query results and the collection view may not return instances of ObservableCollection. They could be just IEnumerables. What is critical is that they implement INotifyCollectionChanged and IEditableCollectionView.
Thanks to those who contributed responses. I've +1'd each response that was helpful, but since none directly solved my problem I couldn't justify marking any as the definitive answer.
Your domainContext will have a property domainContext.MyEntities. Does it not show up in there when you add it?
Bind to that collection or watch that collection for changes.
domainContext.MyEntities.PropertyChanged += MyEventHandler;
I assume you bind your control to the IEnumerable which is provided by LoadOperation<TEntity>.Entities. In that case your binding source is not the DomainContext.GetEntitySet<MyEntity>().
DomainContext.GetEntitySet<MyEntity>() holds all your currently tracked instances of MyEntity, including the one you add with .Add().
LoadOperation<TEntity>.Entities only contains the instances of MyEntity that were actually loaded by your last LoadOperation/Query.
You have two options: Either add the new entity to the ItemsSource-collection for your control (I recommend that) or rebuild the collection with the contents of DomainContext.GetEntitySet<MyEntity>(). That may contain other elements that you have not cleared out before, though.