locked
LINQ to SQL DataContext with custom return type causes data service to crash RRS feed

  • Question

  • Hi all!

    I have a DataService that is backed by a LINQ to SQL model. In the model, I created a custom object that does not directly correlate to any tables in the database. I created this object manually in the designer. It is a aggregation of various columns across multiple tables. This schema is reused across different queries and sprocs so I thought it would be convenient to have a single type to work with across all of them. The L2S works great. I can query it all night and it always returns the proper types.

    The problem that I get arises when I try to use the ADO.NET Data Service I built on top of that L2S. The data service will throw an error whenever it is requested and the custom type is present in the L2S. It happens across the board - via browser, C#, AJAX. If I remove the custom object, the service works again. The error I get from the service is:
    The server encountered an error processing the request. The exception message is 'On data context type 'LogDBDataContext', there is a top IQueryable property 'FilterResults' whose element type is not an entity type. Make sure that the IQueryable property is of entity type or specify the IgnoreProperties attribute on the data context type to ignore this property.'. See server logs for more details.
    where FilterResults is the custom object. All of the other discussions I have seen on this error suggest manually adding a primary key to the code. I tried that briefly but since this object isnt backed by a data source, it 1) doesnt actually have a primary key, 2) causes even more errors when the primary key is null.

    Is there a solution to this that doesnt involve adding a primary key in (or, I guess, adding it in a smarter way than I attempted)?
    Thursday, January 7, 2010 12:27 AM

Answers

  • Hi,

    There are basically two rules which define a class MyClass as an entity.
    1) The class must have a primary key (either DataServiceKeyAttribute or some of the heuristics worked)
    2) The context class must expose a public property with a getter which is of type IQueryable<MyClass> (or some derived type)

    It seems #1 is not a problem (you've added the DataServiceKeyAttribute), but what about the #2. Does you context class expose a property with IQueryable<FilterResult>?

    Thanks,
    Vitek Karas [MSFT]
    • Marked as answer by jeffrod Friday, January 8, 2010 11:18 PM
    Thursday, January 7, 2010 11:20 PM
    Moderator
  • Hi,

    That looks correct. I just noticed that you mentioned that you're getting a different error. What error are you getting now?
    Is it still the one talking about it not being an entity type, or is it the second one without any usefull information?
    The second one looks to me like a problem in your InitializeService mehod. You would get something like this if the InitializeService thrown an exception. Could you please try running this under the debugger set to stop when exception is thrown and see what is the original exception (as the TargetInvocationException is a second in a row).

    Thanks,
    Vitek
    Vitek Karas [MSFT]
    • Marked as answer by jeffrod Friday, January 8, 2010 11:18 PM
    Friday, January 8, 2010 11:10 PM
    Moderator

All replies

  • Hello,

     

    As far as I know, we need to set the DataServiceKey for the ADO.NET Data Service exported entities.  If we don’t set it, the ADO.NET Data Service will use this rule to check whether there is a default key property, http://weblogs.asp.net/sergeyzwezdin/archive/2009/03/24/ado-net-data-services-linq-to-sql-continuation.aspx. 

     

    In your scenario, could you add a new custom property in type of System.Guid inside the custom type?  We can set this property as the key and initialize its value by Guild.NewGuid() when the entity is created. 

     

    If you have any questions, please feel free to let me know.

     

    Have a great day!

     

    Best Regards,
    Lingzhi Sun

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Thursday, January 7, 2010 8:44 AM
    Moderator
  • Thanks for the reply! I tried this out and the error changed. It is now an unfortunately generic error, which is odd because i still have the service set with IncludeExceptionDetailInFaults = true and config.UseVerboseErrors = true.

    Now, when I navigate to my data service in the browser (http://localhost/log/Services/DataService.svc/), I get the following error:

    The server encountered an error processing the request. The exception message is 'Exception has been thrown by the target of an invocation.'. See server logs for more details. The exception stack trace is:

    at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Data.Services.DataServiceConfiguration.InvokeStaticInitialization(Type type) at System.Data.Services.DataService`1.CreateConfiguration(Type dataServiceType, IDataServiceProvider provider, Object dataSourceInstance) at System.Data.Services.DataService`1.CreateProvider(Type dataServiceType, Object dataSourceInstance, DataServiceConfiguration& configuration) at System.Data.Services.DataService`1.EnsureProviderAndConfigForRequest() at System.Data.Services.DataService`1.ProcessRequestForMessage(Stream messageBody) at SyncInvokeProcessRequestForMessage(Object , Object[] , Object[] ) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)


    And the now-modified "auto-generated" designer class is (with minor formatting changes):
    	[Table(Name="")]
    	public partial class FilterResult : INotifyPropertyChanging, INotifyPropertyChanged
    	{
    		
    		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
    		private string _ApplicationName;
    		private string _SeverityName;
    		private int _Matches;
    		private string _Message;
    		private System.DateTime _LastTime;
    		private int _ApplicationID;
    		private string _FilterResultID;
    		
        #region Extensibility Method Definitions
        partial void OnLoaded();
        partial void OnValidate(System.Data.Linq.ChangeAction action);
        partial void OnCreated();
        partial void OnApplicationNameChanging(string value);
        partial void OnApplicationNameChanged();
        partial void OnSeverityNameChanging(string value);
        partial void OnSeverityNameChanged();
        partial void OnMatchesChanging(int value);
        partial void OnMatchesChanged();
        partial void OnMessageChanging(string value);
        partial void OnMessageChanged();
        partial void OnLastTimeChanging(System.DateTime value);
        partial void OnLastTimeChanged();
        partial void OnApplicationIDChanging(int value);
        partial void OnApplicationIDChanged();
        partial void OnFilterResultIDChanging(string value);
        partial void OnFilterResultIDChanged();
        #endregion
    		
    		public FilterResult()
    		{
    			OnCreated();
    		}
    		
    		[Column(Storage="_ApplicationName", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
    		public string ApplicationName { /*irrelevant code*/ }
    		
    		[Column(Storage="_SeverityName", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
    		public string SeverityName { /*irrelevant code*/ }
    		
    		[Column(Storage="_Matches", UpdateCheck=UpdateCheck.Never)]
    		public int Matches { /*irrelevant code*/ }
    		
    		[Column(Storage="_Message", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
    		public string Message { /*irrelevant code*/ }
    		
    		[Column(Storage="_LastTime", UpdateCheck=UpdateCheck.Never)]
    		public System.DateTime LastTime { /*irrelevant code*/ }
    		
    		[Column(Storage="_ApplicationID", UpdateCheck=UpdateCheck.Never)]
    		public int ApplicationID { /*irrelevant code*/ }
    
    		[Column(Storage="_FilterResultID", CanBeNull=false, IsPrimaryKey=true, IsDbGenerated=true, UpdateCheck=UpdateCheck.Never)]
    		public string FilterResultID
    		{
    			get
    			{
    				return this._FilterResultID;
    			}
    			set
    			{
    				if ((this._FilterResultID != value))
    				{
    					this.OnFilterResultIDChanging(value);
    					this.SendPropertyChanging();
    					this._FilterResultID = value;
    					this.SendPropertyChanged("FilterResultID");
    					this.OnFilterResultIDChanged();
    				}
    			}
    		}
    		
    		public event PropertyChangingEventHandler PropertyChanging;
    		public event PropertyChangedEventHandler PropertyChanged;
    		
    		protected virtual void SendPropertyChanging() { /*irrelevant code*/ }
    		protected virtual void SendPropertyChanged(String propertyName) { /*irrelevant code*/ }
    
    		// suggested from http://weblogs.asp.net/stefansedich/archive/2008/01/08/handling-default-values-with-linq-to-sql.aspx
    		partial void OnCreated()
    		{
    			if (this.FilterResultID == null)
    			{
    				this.FilterResultID = Guid.NewGuid().ToString();
    			}
    		}
    	}
    



    Thursday, January 7, 2010 3:27 PM
  • I forgot to specify a couple things in my last post. It might also be helpful to know that this
    1. I created FilterResultID as the primary key for the FilterResult class.
    2. I found a snippet from http://weblogs.asp.net/stefansedich/archive/2008/01/08/handling-default-values-with-linq-to-sql.aspx which I used to set the initial value of the field.
    3. The intent of this FilterResult class is to provide a single definition and object for all of the various ways that that my [Log] objects can be queried (via LINQ, stored procedures, data services, etc).
    Thursday, January 7, 2010 3:40 PM
  • I also tried decorating the FilterResult class with  [DataServiceKey("FilterResultID")] and the returned the same error. I was getting this attribute mixed up with the Linq to SQL attribute [Column(IsPrimaryKey=true)]

    [Table(Name=""), DataServiceKey("FilterResultID")]
    public partial class FilterResult : INotifyPropertyChanging, INotifyPropertyChanged
    

    Thursday, January 7, 2010 8:56 PM
  • Hi,

    There are basically two rules which define a class MyClass as an entity.
    1) The class must have a primary key (either DataServiceKeyAttribute or some of the heuristics worked)
    2) The context class must expose a public property with a getter which is of type IQueryable<MyClass> (or some derived type)

    It seems #1 is not a problem (you've added the DataServiceKeyAttribute), but what about the #2. Does you context class expose a property with IQueryable<FilterResult>?

    Thanks,
    Vitek Karas [MSFT]
    • Marked as answer by jeffrod Friday, January 8, 2010 11:18 PM
    Thursday, January 7, 2010 11:20 PM
    Moderator
  • I believe it does, but can you confirm? There is a lot of junk in there. I cut it out for the sake of brevity, but I can paste it all in if you would like. The class and the getter are below. The getter returns System.Data.Lina.Table<FilterResult> which implements IQueryable<TEntity>. Is that sufficient to fulfill #2?

    	[System.Data.Linq.Mapping.DatabaseAttribute(Name="FH.Logging")]
    	public partial class LogDBDataContext : System.Data.Linq.DataContext
    	{
    		
    		//irrelevant code, i think
    		
    		public System.Data.Linq.Table<FilterResult> FilterResults
    		{
    			get
    			{
    				return this.GetTable<FilterResult>();
    			}
    		}
    		
    		//more irrelevant code, i think
    	}
    

    This class was completely autogenerated from the LINQ designer. I have cut out some code, but the rest I have not modified.

    Jeff
    • Edited by jeffrod Friday, January 8, 2010 9:22 PM clarification on the source of this code
    Friday, January 8, 2010 9:21 PM
  • Hi,

    That looks correct. I just noticed that you mentioned that you're getting a different error. What error are you getting now?
    Is it still the one talking about it not being an entity type, or is it the second one without any usefull information?
    The second one looks to me like a problem in your InitializeService mehod. You would get something like this if the InitializeService thrown an exception. Could you please try running this under the debugger set to stop when exception is thrown and see what is the original exception (as the TargetInvocationException is a second in a row).

    Thanks,
    Vitek
    Vitek Karas [MSFT]
    • Marked as answer by jeffrod Friday, January 8, 2010 11:18 PM
    Friday, January 8, 2010 11:10 PM
    Moderator
  • That pointed me in the right direction! At some point I added a line of code in my random attempts at getting it to work. Specifically tried to set access rules on the FilterResult table. Once I took this line out, the service came back up. Thanks! A combination of adding the DataServiceKey and removing the access rule configuration made everything work!

    Thanks so much!

    	[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true), IgnoreProperties("FilterResults")]
    	public class DataService : DataService<FH.Logging.LogDBDataContext>
    	{
    		public static void InitializeService(IDataServiceConfiguration config)
    		{
    			config.UseVerboseErrors = true;
    			config.SetEntitySetAccessRule("FilterResult", EntitySetRights.None);     //this line cause the service to throw the second error
    			config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
    		}
    	}
    


    Friday, January 8, 2010 11:18 PM
  • Hi,

    Yes the EntitySetRights.None is a bit specific. It basically makes the FilterResult (and its type) to disappear. So asking for it should get you 404 reponses. Also note that specific access rules always win over the general "*" rule.

    Thanks,
    Vitek Karas [MSFT]
    Friday, January 8, 2010 11:53 PM
    Moderator