Calculated field in model class gives exception - c#

I'm getting the exception when executing Linq query:
The specified type member 'Active' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
Model class
public class User : Entity
{
//Many properties skipped
[NotMapped]
public bool Active
{
get
{
return Orders.Any(c =>
c.Active && (c.TransactionType == TransactionType.Order ||
c.TransactionType == TransactionType.Subscription));
}
}
}
The Linq query which gives exception
public IEnumerable<User> GetInactiveUsersForSuspensionNotification()
{
return _userRepository.GetAll()
.Include(i=>i.Orders)
.Where(w => w.Active == false);
}
Orders is a related table to Users.

When using LINQ to Entities the LINQ expression is converted to a SQL query to be sent to the database. This is so that the whole table is not pulled into memory.
Your problem is that because Active is not mapped the database knows nothing about it and therefore cannot make the calculation.
You will either need to move Active into your DB or change the LINQ statement to only query on columns that are in your DB

Related

AsNoTracking throwing error Method 'System.Data.Entity.Infrastructure.DbQuery declared on type For DbSet with collection inside

I have an object
public partial class FileAttachment
{
public FileAttachment()
{
this.Tags = new HashSet<Tag>();
}
public virtual ICollection<Tag> Tags {get; set;}
...other propertties
}
My context class is having
public virtual DbSet<FileAttachment> FileAtttachments {get; set;}
public virtual DbSet<Tag> Tags {get; set;}
public virtual DbSet<Item> Items {get; set;}
...
I am using it in my controller like
MyContext.Items.AsNoTracking().Where(i => i.ItemId == inputId &&
MyContext.FileAttachments.AsNoTracking.Where(f => f.RecordId == i.ReferenceId && f.RecordType == 'item').Any()).Any();
The error I am getting is
'Method 'System.Data.Entity.Infrastructure.DbQuery1[Demo.FileAttachment] AsNoTracking()' declared on type 'System.Data.Entity.Infrastructure.DbQuery1[Demo.FileAttachment]' cannot be called with instance of type 'System.Data.Entity.Core.Objects.ObjectQuery`1[Demo.FileAttachment]''
I am new to AsNoTracking(). Anyone can tell where I am doing wrong?
It looks like the actual requirement is to check whether an Item with a specific ID has a related FileAttachment of a specific type. There's no need for AsNoTracking() for this since the query returns a single boolean, not objects that need tracking.
If the classes had proper navigation properties, the query would be a simple:
var found= MyContext.FileAttachments.Any(f=>f.RecordType == 'item'
&& f.Item.Id = ThatItemID);
Without them, the LINQ query will have to JOIN the two "unrelated" entities:
var query = from item in MyContext.Items
join person in MyContext.FileAttachmentes
on file.RecordId equals item.ReferenceId
where item.ItemID == ThatId &&
file.RecordType == 'item'
select 1;
var found=query.Any();
The lack of navigation properties is a very strong sign that the model is wrong though.
A DbContext isn't a model of the database and doesn't have to mirror it. There's no need to have a single DbContext for the entire application either. Even if there are no Foreign Key Constraints between Items and FileAttachments it's possible to add relations between the classes
AsNoTracking() is used to prevent entity tracking by EF. Tracking allows EF to keep track of modifications made and on saving, these changes can be persisted. So, if a query doesn't need tracking (like a simple get query where no data change is persisted), we use AsNoTracking().
In your case, use navigation property if the entities are related instead of calling Context.Entity. Also, AsNoTracking() needs to specified only once in the query before loading the data.

Find all entries where nullable DateTime contains keyword

I have a model with a nullable DateTime. I'm trying to use an IQueryable object and find all entries where the DateTime matches to a string, if set:
query.Where(s => s.MyDate.HasValue && s.MyDate.Value.ToString("{ 0:dd.MM.yyyy}").Contains(keyword));
However this doesn't work as an exception is thrown: LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
As I've done some research, the problem seems to be that my where condition can't be translated to SQL.
How can I solve this problem?
Example data shown to the user:
10.03.2017
01.08.2017
Possible search terms:
08
08.07.
08.07.2017
8.07.2017
...
Since you aren't concerned about date formats (assuming the user knows what they are querying) just use the SQL Server default conversion:
query.Where(s => s.MyDate.HasValue && s.MyDate.Value.ToString().Contains(keyword));
In case you're using the Entity Framework: Lets assume s is type DemoClass:
public partial class DemoClass
{
public Nullable<DateTime> MyDate;
...
}
would be the DemoClass.cs for your Entity. Just have an additional partial class in an extra file (this is the best way otherwise EF could override your edits if you're using the designer e.g.):
DemoClass_Additional.cs
public partial class DemoClass
{
[NotMapped]
public string MyDateString {
get
{
if(this.MyDate.HasValue)
{
return this.MyDate.Value.ToString("{ 0:dd.MM.yyyy}");
}
else
{
return "";
}
}
}
[NotMapped] will exclude the property from the database mapping and finally your query would be
query.Where(s => s.MyDate.HasValue && s.MyDateString.Contains(keyword));

Dynamic OrderBy() using property not in DB throws exception

I have the following models:
public class User
{
public string UserId { get; set; }
public string UserName { get; set; }
public List<Membership> Membership { get; set; }
public bool IsRegistered
{
get { return !String.IsNullOrEmpty(UserName); }
}
}
public class Membership
{
public string MembershipId { get; set; }
public User User { get; set; }
}
In my repository for the Membership entity, I'm attempting to order my collection by doing the following where entries is an IQueryable<Membership> and orderBy is a LamdaExpression equal to { x => x.User.IsRegistered }:
ordered = Queryable.OrderBy(ordered, orderBy);
When I attempt this, I get the following error:
The specified type member 'IsRegistered' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
How can I order my collection by a property that is not in the database?
Thanks in advance.
Since IsRegistered is a client side function, then you need to do the ordering using LINQ over objects. You can convert your LINQ over SQL to LINQ over objects by causing it to get enumerated. Then once it's a LINQ over objects, you can then order it using LINQ over obect's OrderBy. A common method of doing that is by calling ToList, like:
ordered = Queryable.ToList().OrderBy(ordered, orderBy);
If you want to do the ordering on the database, then you need to convert your clientside code to one that is compatible to SQL, like:
ordered = Queryable.OrderBy(ordered, (x=>x.User.UserName!=null && x.User.UserName!=''));
You can always sort locally. If you want a calculated property to be used for server side ordering you'd need an expression that your LINQ provider (EF) can translate (into SQL in your case).
For your !String.IsNullOrEmpty(UserName) you could use m=>String.Empty!=(m.User.UserName??String.Empty) which works for LINQ2SQL. When checking the generated SQL you should get something like
-- Region Parameters
DECLARE #p0 NVarChar(1000) = ''
DECLARE #p1 NVarChar(1000) = ''
-- EndRegion
SELECT --fields
FROM [Membership] as [t0]
JOIN User AS [t1]
ON -- id
WHERE -- your predicate
ORDER BY
(CASE
WHEN #p0 <> (COALESCE([t1].[UserName],#p1)) THEN 1
WHEN NOT (#p0 <> (COALESCE([t1].[UserName],#p1))) THEN 0
ELSE NULL
END)
I guess EF also translates that likewise.
You can't. Entity framework's order by is mapped to db fields because it's calling "ORDER BY" clause on SQL side.
If you really want to order your records by a non-db field (however, as far as I can see, you can just order your records by UserName in this situation), you should filter your records, call ToList() and OrderBy on that (obviusly this only order your records after the result returned from the database server).

Limit collection to retrieve only recent entries for readonly entity

The User entity can have thousands of UserOperations. Sometimes I don't want to retrieve (for readonly entity) all of them but only "the recent 10 OR not completed".
public class SimpleForm
{
public class User : EntityBase
{
// ...
private ISet<UserOperation> _recentOperations = new HashedSet<UserOperation>();
public virtual ISet<UserOperation> RecentOperations { get { return _recentOperations; } set { _recentOperations = value; } }
}
}
So how can I specify it? I think I could use mapping overrides?
I understand I could make this with a seperate query but can it be done by entity mapping?
Also I wonder if there is a possibility to do the some for non-readonly entity where I can modify the collection of operations?
UPDATE
I tried to use
DateTime dateTime = (DateTime.UtcNow - TimeSpan.FromDays(15));
mapping.HasMany(x => x.RecentOperations)
.Where(x => x.EndedAt == null || x.EndedAt < dateTime);
but it says "Unable to convert expression to SQL".
I replaced it with
mapping.HasMany(x => x.RecentOperations)
.Where(x => x.EndedAt == null);
and now it throws null reference exception inside
в FluentNHibernate.Utils.ExpressionToSql.Convert(Object value)
в FluentNHibernate.Utils.ExpressionToSql.Convert(ConstantExpression expression)
в FluentNHibernate.Utils.ExpressionToSql.Convert[T](Expression`1 expression, UnaryExpression body)
There are 2 general ways how to filter mapped collections.
The first is a bit rigid, fixed, in a mapping defined where="" clause:
6.2. Mapping a Collection (...in fluent .Where(bool expr) or .Where(Sql statement string)
The second and maybe really suitable in this scenario, is dynamic version called filter:
18.1. NHibernate filters
NHibernate adds the ability to pre-define filter criteria and attach those filters at both a class and a collection level. A filter criteria is the ability to define a restriction clause very similiar to the existing "where" attribute available on the class and various collection elements. Except these filter conditions can be parameterized. The application can then make the decision at runtime whether given filters should be enabled and what their parameter values should be. Filters can be used like database views, but parameterized inside the application....
The implementation in fluent would look like this:
public class RecentFilter : FilterDefinition
{
public RecentFilter()
{
WithName("RecentFilter")
.WithCondition("( :EndedAtDate IS NULL OR EndedAt < :EndedAtDate )")
.AddParameter("EndedAtDate",NHibernate.NHibernateUtil.DateTime);
}
}
this is the filter, and here is its usage in a fluent mapping:
mapping
.HasMany(x => x.RecentOperations)
...
.ApplyFilter<RecentFilter>();
In runtime, we can turn filter on/off on the ISession level:
session.EnableFilter("RecentFilter")
.SetParameter("EndedAtDate",DateTime.Now.AddDays(-15));
See also:
property filter with fluent nHibernate automapping
Syntax to define a NHibernate Filter with Fluent Nhibernate?
Is it possible to use NHibernate Filters to filter through references?

LinQ to SQL ignoring != null on virtual properties

I've been using LinQ to SQL with EF for a while now and have just stumbled on some strange behaviour that hopefully someone may be able to explain for me.
I'm performing a LinQ query on a database context that has a POCO entity with virtual properties for related entities. I'm using a where clause to eliminate instances where that entity is null. I'm using Lazy Loading.
return this.runtimeContext.FatalExceptionLogs.Where(l => l.RuntimeToDesignJuicerRelationship != null);
What I'm finding is that when my query is evaluated LinQ to SQL seems to entirely ignore my condition that the virtual property is null as if I'd never included this check at all. Instead it returns all records in my dbset named FatalExceptionLogs.
Now I have a simple workaround for this which is to first load the data into memory using .ToList() then
This looks like so:
return this.runtimeContext.FatalExceptionLogs.ToList().Where(l => l.RuntimeToDesignJuicerRelationship != null);
Now the check is performed in memory and all instances where the virtual property is null are returned (because there is no corresponding record as the id which is used for the join is nullable) and we're all good.
I have also considered:
Checking if the id that is joined on is null but unfortunately I can't garauntee that the referential integrity of the table has been maintained as there is no foreign key applied urgh!.
Checking if there are any records in the other table with the matching id, but that could be rather inefficient.
So I have a way of working around this but I'd really like to understand why LinQ to Sql is doing this and what other options there are, can anyone help?
The full code if it helps is below though I've cut it down for this example:
The query:
return this.runtimeContext.FatalExceptionLogs.ToList().Where(l => l.RuntimeToDesignJuicerRelationship != null);
The entity:
public class FatalExceptionLog
{
public int Id { get; set; }
public int? RuntimeJuicerId { get; set; }
public virtual RuntimeToDesignJuicerRelationship RuntimeToDesignJuicerRelationship { get; set; }
}
The mapping:
public class FatalExceptionLogMap : EntityTypeConfiguration<FatalExceptionLog>
{
public FatalExceptionLogMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("FatalExceptionLogging");
this.Property(t => t.RuntimeJuicerId).HasColumnName("JuicerLiveID");
this.HasRequired(t => t.RuntimeToDesignJuicerRelationship)
.WithMany(t => t.FatalExceptionLogs)
.HasForeignKey(t => t.RuntimeJuicerId);
}
}
Why NOT just do the normal joining?
return this.runtimeContext.FatalExceptionLogs.Where(
l => runtimeContext.RuntimeJuicers.Any(
y => y.RuntimeJuicerId == l.RuntimeJuicerId
)
);

Categories