Is it possible to query on an interface property? - c#

I have the following class:
public class BicycleSellerListing : IUserName
{
public UserProfile UserProfile { get; set; }
/// <summary>
/// IUserName interface property
/// </summary>
public string UserName
{
get
{
return UserProfile.UserName;
}
}
}
The interface:
public interface IUserName
{
string UserName { get; }
}
And this query:
public static List<T> GetList<T>(string userName) where T : class, IUserName
{
using (SqlUnitOfWork work = new SqlUnitOfWork())
{
return work.GetList<T>(row => row.UserName == userName)
.ToList();
}
}
When I execute this query, I get the following exception:
The specified type member 'UserName' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
I understand why I'm getting the exception, but am wondering if there is a way I can perform this query using the interface?

To answer you quistion:
Is it possible to query on an interface property?
Yes. it's no problem. The fault you are getting is not becuase of the interface.
The problem is that you can't query on properties that isn't mappe with Linq 2 Entities
Other people have had this propblem as well.
The underlying expressionbuilder can not distinct between properties that is mapped to the database, and properties that is not.
It is a problem becuase the compiler can't help you.
In Linq to object, it is no problem, so the compiler doesnt throw any errors/warnings
You should try to make it clear the this property is not mapped - perhaps by a prefix, or a nested class that contains all the "custom" properties.

In addition to the existing answer(s), you can perform the where in memory, but that means retrieving the whole table.
Normally I wouldn't recommend this though.
public static List<T> GetList<T>(string userName) where T : class, IUserName
{
using (SqlUnitOfWork work = new SqlUnitOfWork())
{
return work.GetList<T>()
.AsEnumerable()
.Where(row => row.UserName == userName)
.ToList();
}
}

Some workarounds:
You could try to determine underlying entity type in runtime and
then dynamically compile and invoke a generic method executing query
with this type.
It is possible to assemble this query manually in runtime using
Expression class
You could try Entity SQL query syntax instead of Linq

Related

Query string properties stored as XML

I am using Entity Framework to query a db which is defined by a model: inside this model I have several classes having a #region dynamic values:
[DataContract]
public class Job : AbstractEntity, IJob
{
[DataMember]
public virtual Guid Id { get; set; }
...
#region dynamic values
[DataMember]
public virtual string MetadataValue { get; set; }
[DataMember]
public virtual string ParametersValue { get; set; }
[DataMember]
public virtual string AttributesValue { get; set; }
#endregion
#region links
...
#endregion
}
AttributesValue, MetadataValue and ParametersValue are declared as string but are stored inside the db as XML documents. I am aware that this is not consistent with the model and should be changed, but for some reasons it has been managed this way and I am not allowed to modify it.
I have created a Unit Test in order to better handle the problem, and here is the code:
public class UnitTest1
{
private ModelContext mc;
[TestInitialize]
public void TestInit()
{
IModelContextFactory mfactory = ModelContextFactory.GetFactory();
mc = mfactory.CreateContextWithoutClientId();
}
[TestMethod]
public void TestMethod1()
{
DbSet<Job> jobs = mc.Job;
IQueryable<string> query = jobs
.Where(elem => elem.AttributesValue == "<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>")
.Select(elem => elem.AttributesValue);
List<string> attrs = new List<string>(query);
foreach (string av in attrs)
{
Console.WriteLine(av ?? "null");
}
Assert.AreEqual(1, 1);
}
}
A quick explanation about the TestInit and ModelContext:
ModelContext inherit from DbContext and is an abstract class implemented by SqlModelContext and OracleModelContext (both override OnModelCreating). Depending on the connection string, CreateContextWithoutClientId return a SqlModelContext or an OracleModelContext. Summary: a Factory pattern.
Let's get down to brass tacks: the TestMethod1.
The problem here is in the Where method and the error returned is, as expected:
SqlException: The data types nvarchar and xml are incompatible in the equal to operator.
(From now on I will only consider the AttributesValue property)
I thought of some possible solutions, which are:
Creating a new property inside the model (but not mapped to the db) and use it as a "proxy" instead of accessing directly AttributesValue. However only mapped properties can be used in Linq, so I discarded it.
Operating directly on the inner SQL query generated by the IQueryable and using a customized CAST for Oracle and Sql Server db. I'd rather avoid go for this for obvious reasons.
Is there a way to specify a custom Property Getter so that I can cast AttributesValue to string before it is accessed? Or maybe some configuration on the DbModelBuilder?
I'm using standard Entity Framework 6, Code-First approach.
There is no standard xml data type or standard canonical function for converting string to xml or vice versa.
Fortunately EF6 supports the so called Entity SQL Language which supports an useful construct called CAST:
CAST (expression AS data_type)
The cast expression has similar semantics to the Transact-SQL CONVERT expression. The cast expression is used to convert a value of one type into a value of another type.
It can be utilized with the help of the EntityFramework.Functions package and Model defined functions.
Model defined functions allow you to associate Entity SQL expression with user defined function. The requirement is that the function argument must be an entity.
The good thing about Entity SQL operators is that they are database independent (similar to canonical functions), so the final SQL is still generated by the database provider, hence you don't need to write separate implementations for SqlServer and Oracle.
Install the EntityFramework.Functions package through Nuget and add the following class (note: all the code requires using EntityFramework.Functions;):
public static class JobFunctions
{
const string Namespace = "EFTest";
[ModelDefinedFunction(nameof(MetadataValueXml), Namespace, "'' + CAST(Job.MetadataValue AS String)")]
public static string MetadataValueXml(this Job job) => job.MetadataValue;
[ModelDefinedFunction(nameof(ParametersValueXml), Namespace, "'' + CAST(Job.ParametersValue AS String)")]
public static string ParametersValueXml(this Job job) => job.ParametersValue;
[ModelDefinedFunction(nameof(AttributesValueXml), Namespace, "'' + CAST(Job.AttributesValue AS String)")]
public static string AttributesValueXml(this Job job) => job.AttributesValue;
}
Basically we add simple extension method for each xml property. The body of the methods doesn't do something useful - the whole purpose of these methods is not to be called directly, but to be translated to SQL when used inside LINQ to Entities query. The required mapping is provided through ModelDefinedFunctionAttribute and applied via package implemented custom FunctionConvention. The Namespace constant must be equal to typeof(Job).Namespace. Unfortunately due to the requirement that attributes can use only constants, we can't avoid that hardcoded string as well as the entity class / property names inside the Entity SQL string.
One thing that needs more explanation is the usage of '' + CAST. I wish we could use simply CAST, but my tests show that SqlServer is "too smart" (or buggy?) and removes the CAST from expression when used inside WHERE. The trick with appending the empty string prevents that behavior.
Then you need to add these functions to entity model by adding the following line to your db context OnModelCreating override:
modelBuilder.AddFunctions(typeof(JobFunctions));
Now you can use them inside your LINQ to Entities query:
IQueryable<string> query = jobs
.Where(elem => elem.AttributesValueXml() == "<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>")
.Select(elem => elem.AttributesValue);
which translates to something like this in SqlServer:
SELECT
[Extent1].[AttributesValue] AS [AttributesValue]
FROM [dbo].[Jobs] AS [Extent1]
WHERE N'<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>'
= ('' + CAST( [Extent1].[AttributesValue] AS nvarchar(max)))
and in Oracle:
SELECT
"Extent1"."AttributesValue" AS "AttributesValue"
FROM "ORATST"."Jobs" "Extent1"
WHERE ('<coll><item><key>ids:ui:description</key><value>Session Test</value></item><item><key>ids:all:type</key><value>signature</value></item></coll>'
= ((('')||(TO_NCLOB("Extent1"."AttributesValue")))))

Entity Framework, Generic loading of related entities with a where condition

I'm trying to create a function that generically loads the related child entities with a filter.
All my entities are derived from my own Base Class "BusinessObject"
public abstract class BusinessObject : BaseObject, IBaseObject, ILocalObject
{
[Browsable(false)]
[Key]
public int ID { get; set; }
[Browsable(false)]
public int? HqID { get; set; }
private bool _deleted;
[Browsable(false)]
public bool Deleted
{
get { return _deleted; }
set { CheckPropertyChanged(ref _deleted, value); }
}
}
I have created the following function that when supplied an entity will load all the related child objects.
When defining my entities, all child collections are flagged by my own attribute "EntityChildCollectionAttribute" so I can easily find the collections I want to load.
public virtual void OnLoadEntityChildren(object entity)
{
var propNames = entity.GetPropertyNames();
foreach (var propName in propNames.Where(propName => entity.PropertyHasCustomAttribute(propName, typeof(EntityChildCollectionAttribute))))
{
MyData.Entry(entity).Collection(propName).Load();
}
}
This works lovely!
My Problem comes when I want to filter the child collection.
In this case I want to only load child entities where Deleted == false.
I cannot work out how to do this!
I have had many attempts and replacing MyData.Entry(entity).Collection(propName).Load(); with
MyData.Entry(entity).Collection(propName).Query().Cast<BusinessObject>().Where(x=>x.Deleted.Equals(false)).Load();
compiles but then I get the error;
"Unable to cast the type 'FmOrderProcessing.Entities.OpDocumentDetail' to type 'FwBaseEntityFramework.BusinessObject'. LINQ to Entities only supports casting EDM primitive or enumeration types."
Any Help/Pointers/Answers will be gratefully received
Thanks in advance
Lance
I was implementing a "Soft Delete" pattern which means the records in the database are flagged as deleted rather than removed (for audit and replication purposes).
All entities are derived from a base definition with a bool Deleted property.
I found the answer here:
https://www.nuget.org/packages/EntityFramework.DynamicFilters
This package allows the definition of global filters in the data context.
I fixed my issue with one line of code in the OnModelCreating override.
modelBuilder.Filter("Deleted", (IBaseObject d) =>d.Deleted, false);
The filter function is applied globally to any entity presenting (in my case) the IBaseObject interface.
I hope this helps any body else with a similar issue

How do I rewrite query expressions to replace enumerations with ints?

Inspired by a desire to be able to use enumerations in EF queries, I'm considering adding an ExpressionVisitor to my repositories that will take incoming criteria/specifications criteria and rewrite them to use the corresponding persisted int property.
I'm consistently using the following Value-suffix pattern in my (code-first) entities:
public class User : IEntity
{
public long ID { get; set; }
internal int MemberStatusValue { get; set; }
public MemberStatus MemberStatus
{
get { return (MemberStatus) MemberStatusValue; }
set { MemberStatusValue = (int) value; }
}
}
And map this to the database using the following:
internal class UserMapping : AbstractMappingProvider<User>
{
public override void DefineModel( DbModelBuilder modelBuilder )
{
// adds ToTable and other general mappings
base.DefineModel( modelBuilder );
Map.Property( e => e.MemberStatusValue ).HasColumnName( "MemberStatus" );
}
}
In my repositories I have the following method:
public IQueryable<T> Query( Expression<Func<T, bool>> filter, params string[] children )
{
if( children == null || children.Length == 0 )
{
return Objects.Where( filter );
}
DbQuery<T> query = children.Aggregate<string, DbQuery<T>>( Objects, ( current, child ) => current.Include( child ) );
return filter != null ? query.Where( filter ) : query;
}
I'd like to add a method call inside this method to rewrite the filter expression, replacing all references to the MemberStatus property with references to MemberStatusValue.
I suppose it will be a solution involving something like seen in this SO post, but I'm not sure exactly how to get from idea to implementation.
If you can give any advice on the potential performance impact of adding this feature, that would also be appreciated.
I'm not sure whether this is quite what you're after, but I've found it simpler to handle enums in a similar but slightly different way. To wit, I have two properties, as you do, but my int property is public and is what the database persists; I then have another public "wrapper" property that gets/sets the int property value via casts from/to the desired enumerated type, which is what's actually used by the rest of the application.
As a result, I don't have to mess around with the model; EF understands and persists the int property just fine while the rest of the application gets nice interactions with the enum type. The only thing I don't like about my approach is that I have to write my LINQ statements with a bunch of casts on any enum value I'm trying to query to turn it into an int to match against the field that's actually persisted. It's a small price, however, and I'd like to suggest it to you because it appears to me that you're using a string to generate your query which gives up all the type safety, Intellisense, etc. that LINQ provides.
Finally, if you're interested in a walkthrough of how to use the new enum features in EF 5 (which is available in beta for download now if you'd like to try it out), check this out:
http://msdn.microsoft.com/en-us/hh859576

Can I create "relay" or "genericized" properties for Entity Framework models?

I hope my wording makes sense... I wasn't quite sure exactly how to explain what I'm looking to do.
I have a method in a generic class that returns a list of entities as follows:
public abstract class ChildCRUDController<TModel> : CRUDController<TModel, ... >
where TModel : IChildEntity
public ViewResult List(int id)
{
return View(repository.GetMany(x => x.ParentID == id));
}
This controller is implemented by quite a few other controllers. The issue I have is that not all entities that implement IChildEntity have the same parent type. To get around this issue I created ParentID properties for all the models that implement IChildEntity so they could use the same controller.
public partial class PhoneNumber : IChildEntity
{
public int ParentID
{
get { return CustomerID; }
set { CustomerID = ParentID; }
}
}
and...
public partial class Transaction : IChildEntity
{
public int ParentID
{
get { return LeaseID; }
set { LeaseID= ParentID; }
}
}
But when I call the List method above I get the following error:
The specified type member 'ParentID' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Is there any way I can achieve the result I am looking for without pulling the object set into memory or renaming all the properties on the entities themselves?
Thanks!
If you are willing to pass the field name into the List method and to construct your own query you can do it using the techniques described in this StackOverflow article:
Querying Entity with LINQ using Dyanmic Field Name
Or you could supply the ChildCRUDController with another generic type parameter constrained to an interface that supplies the field name and again use it dynamically.

LINQ, entity that implements Interface and exception in mapping

I'm using repository pattern with LINQ, have IRepository.DeleteOnSubmit(T Entity). It works fine, but when my entity class has interface, like this:
public interface IEntity { int ID {get;set;} }
public partial class MyEntity: IEntity {
public int ID {
get { return this.IDfield; }
set { this.IDfield=value; }
}
}
and then trying to delete some entity like this:
IEntity ie=repository.GetByID(1);
repoitory.DeleteOnSubmit(ie);
throws
The member 'IEntity.ID' has no supported translation to SQL.
fetching data from DB works, but delete and insert doesn't. How to use interface against DataContext?
Here it is:
Exception message:
The member 'MMRI.DAL.ITag.idContent' has no supported translation to SQL.
Code:
var d = repContent.GetAll().Where(x => x.idContent.Equals(idContent));
foreach (var tagConnect in d) <- error line
{
repContet.DeleteOnSubmit(tagConnect);
(it gets all tags from DB, and deletes them)
And stack trace:
[NotSupportedException: The member 'MMRI.DAL.ITag.idContent' has no supported translation to SQL.]
System.Data.Linq.SqlClient.Visitor.VisitMember(SqlMember m) +621763
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) +541
System.Data.Linq.SqlClient.SqlVisitor.VisitExpression(SqlExpression exp) +8
System.Data.Linq.SqlClient.SqlVisitor.VisitBinaryOperator(SqlBinary bo) +18
System.Data.Linq.SqlClient.Visitor.VisitBinaryOperator(SqlBinary bo) +18
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) +196
System.Data.Linq.SqlClient.SqlVisitor.VisitExpression(SqlExpression exp) +8
System.Data.Linq.SqlClient.SqlVisitor.VisitSelectCore(SqlSelect select) +46
System.Data.Linq.SqlClient.Visitor.VisitSelect(SqlSelect select) +20
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) +1024
System.Data.Linq.SqlClient.SqlProvider.BuildQuery( ...
When I try do decorate partial class:
[Column(Storage = "_idEvent", DbType = "Int NOT NULL", IsPrimaryKey = true)]
public int idContent
{ get { return this.idEvent; } set { this.idEvent=value; } }
it throws error "Invalid column name 'idContent'."
It appears Microsoft dropped support for == operator in interfaces when using linq-to-sql in MVC4 (or maybe it was never supported). You can however use i.ID.Equals(someId) in place of the == operator.
Casting IQueryable to IEnumerable works but should not be used! The reason is: IQueryable has funky implementation of IEnumerable. Whatever linq method you'll use on a IQueryable through the IEnumerable interface will cause the query to be executed first, have all the results fetched to the memory from the DB and eventually running the method localy on the data (normally those methods would be translated to SQL and executed in the DB). Imagine trying to get a single row from a table containing billion rows, fetching all of them only to pick one (and it gets much worse with careless casting of IQueryable to IEnumerable and lazy loading related data).
Apparently Linq has no problem using == operator with interfaces on local data (so only IQueryable is affected) and also with Entity Frameworks (or so I heard).
This works for me -
public partial class MyEntity: IEntity
{ [Column(Name = "IDfield", Storage = "_IDfield", IsDbGenerated = true)]
public int ID
{
get { return this.IDfield; }
set { this.IDfield=value; }
}
}
Try this:
using System.Data.Linq.Mapping;
public partial class MyEntity: IEntity
{ [Column(Storage="IDfield", DbType="int not null", IsPrimaryKey=true)]
public int ID
{
get { return this.IDfield; }
set { this.IDfield=value; }
}
}
For translating your LINQ query to actual SQL, Linq2SQL inspects the expression you give it. The problem is that you have not supplied enough information for L2S to be able to translate the "ID" property to the actual DB column name. You can achieve what you want by making sure that L2S can map "ID" to "IDField".
This should be possible using the approach provided in answers.
If you use the designer, you can also simply rename the class property "IDField" to "ID", with the added benefit that you won't have to explicitly implement the "ID" property in your partial class anymore, i.e. the partial class definition for MyEntity simply becomes:
public partial class MyEntity: IEntity
{
}

Categories