Defining a consistent projection in a model with EF6 - c#

Background
We have a table, let's call it Files.
We have certain attributes on the row, such as Name, CreatedDate, etc.
We have a blob column with the contents of the file, FileBytes.
So our model looks similar to:
public class FileEntity
{
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public byte[] FileBytes { get; set; }
// many other fields, most of which we'd like to use
}
Goal
On certain queries, we only care about whether FileBytes is null, not anything about the bytes themselves.
We'd like to be able to query and have a field in our model class populated, say, FileHasBytes that is a bool.
We'd like this field to exist only in our class, so that we can refer to it in the webapp as part of the model.
We'd like to be able to query for this bool value without pulling the full bytes from the field.
Question
How can I, using EF6, define a field on my model class that will be consistently projected, based on another field in the table, without pulling the full contents of that field?
Considered Options / Workarounds
Computed column: we were hoping to avoid this because it seems unnecessary
View: We'd also like to avoid this because it seems unnecessary to go to this for a single column
Projection to a new object: This is doable, but we'd like to be able to map directly without selecting a new object each time, including all of the fields that go with it.

With the current version of EF6 you can't do exactly what you are after.
There are other alternatives, but with all of them you'd have to make compromises on the goals stated above. Such as either using a new projected type with the computed property, or not querying on the computed value and be explicit on the condition instead, etc.
However, using something like DelegateDecompiler it might be possible to have the model and query just as you are expecting.
Something along the lines of:
[Computed]
public bool HasFileBytes
{
get { return FileBytes != null; }
}
And in your query you'd use the .Decompile() call to get that translated:
var query = ctx.Files
.Where(x => x.HasFileBytes)
.Decompile();
Using Linq.Translations could be another similar alternative.
source: http://www.daveaglick.com/posts/computed-properties-and-entity-framework

This is not ideal but I think you can add a static property that returns QueryExpression like
public static Expression<Func<FileEntity,bool>> FileHasBytes
{
get { return ((c)=> c.FileBytes != null && SqlFunctions.DataLength(c.FileBytes)>0)
}
I have not tried this code, so take this with grain of salt, so try and test it thoroughly.
I have used some thing like this using Dynamic.linq some time ago, but not tried it lately
dbContext.FileEntities.Where("FileHasBytes == true"),

Related

How to convert a string to decimal within an IQueryable

Well, this sounds easier than it is! I have a table that contains amount stored as string values. But my code needs them as decimal values and it needs them to be casted to decimal as part of the query that IQueryable is making! Thus, the use of .ToList() is not possible. The cast needs to be done by the database behind it.
This IQueryable is basically part of a chain on IQueryables so a next query might want to add a filter on this amount to make a smaller selection, or do other kinds of math with the amount. The query that goes in might already be a subselection of all data.
Edit: Let me explain what I am working on here: I have something like this method to write:
public static IQueryable<PriceList> GetPriceList(this IQueryable<PriceData> query) => query.Select(d => new PriceList{Name = d.Item.Name, Price = decimal.Parse(d.Value)}).AsQueryable();
And PriceData is a record containing a few fields and the prices as string values. But the PriceList record needs them as Decimal.
This method could then be used by another extension method for further selections, math and whatever. It's just that decimal.parse and other options don't work within an IQueryable...
As already commented, what you are trying to do is not supported.
Your best bet is to change your database field type to what it actualy represents: Money/Decimal
If that is not possible, talk to the person, which has the ability to make it possible
If that is not possible, the closest workaround I can imagine is:
public class PriceList
{
public string Price { get; set; }
public decimal ParsedPrice => decimal.Parse(Price);
}
And then:
.Select(d=>new PriceList
{
Price = d.Price
});
However, you can't execute any DbOperations on ParsedPrice then. Also note that you can't use a Constructor with parameters in EF6. That would be the next problem you would encounter.
EF works best for CRUD. For everything more you probably should have a Service, which is fetching the data only you need, materialize them and make a DTO or Businessobject out of it.
Something like that:
public class MyService
{
public IEnumerable<PriceList> GetPriceList(Expression<Func<MyEntity, bool>> predicate)
{
var data = _context.MyEntity.Where(predicate).ToList();
foreach (var item in data)
{
var dto = new PriceList {...}
yield return dto;
}
}
}

ADO.NET build complex objects using DataReader

I'm trying to create a way to make an unique search into the database and build the right object for my needs. I mean, I use a SQL query that returns me a lot of rows and then I build the collections based on that database rows. E.g.:
We have a table called People and another table called Phones.
Let's suppose that this is my SQL query and will return the following below:
SELECT
P.[Id], P.[Name], PH.[PhoneNumber]
FROM
[dbo].[People] P
INNER JOIN
[dbo].[Phones] PH ON PH.[Person] = P.[Id]
And that's the results returned:
1 NICOLAS (123)123-1234
1 NICOLAS (235)235-2356
So, my class will be:
public interface IModel {
void CastFromReader(IDataReader reader);
}
public class PhoneModel : IModel {
public string PhoneNumber { get; set; }
public PhoneModel() { }
public PhoneModel(IDataReader reader) : this() {
CastFromReader(reader);
}
public void CastFromReader(IDataReader reader) {
PhoneNumber = (string) reader["PhoneNumber"];
}
}
public class PersonModel : IModel {
public int Id { get; set; }
public string Name { get; set; }
public IList<PhoneModel> Phones { get; set; }
public PersonModel() {
Phones = new List<PhoneModel>();
}
public PersonModel(IDataReader reader) : this() {
CastFromReader(reader);
}
public void CastFromReader(IDataReader reader) {
Id = Convert.ToInt32(reader["Id"]);
Name = (string) reader["Name"];
var phone = new PhoneModel();
phone.CastFromReader(reader);
Phones.Add(phone);
// or
Phones.Add(new PhoneModel {
PhoneNumber = (string) reader["PhomeNumber"]
});
}
}
This code will generate a PersonModel object with two phone numbers. That's good so far.
However, I'm struggling to make some good way to deal when I want to manage more tables with this process.
Let's suppose, then, I have a new table called Appointments. It stores the user's appointments to the schedule.
So, adding this table to the query, the result will be:
1 NICOLAS (123)123-1234 17/09/2014
1 NICOLAS (123)123-1234 19/09/2014
1 NICOLAS (123)123-1234 27/09/2014
1 NICOLAS (235)235-2356 17/09/2014
1 NICOLAS (235)235-2356 19/09/2014
1 NICOLAS (235)235-2356 17/09/2014
As you guys can see, the problem is to manage the phones and the appointments this way. Do you can think in anything that could solve this issue?
Thank you all for the opinions!
You cannot transfer your query result to strongly typed objects without first defining these objects' types. If you want to keep query data in memory, I recommend that you transfer it into objects of a previously defined type at some point.
What follows is therefore not something that I would actually recommend doing. But I want to demonstrate to you a possibility. Judge for yourself.
As I suggested in a previous comment, you can mimick strongly typed DTOs using the Dynamic Language Runtime (DLR), which has become available with .NET 4.
Here is an example for a custom DynamicObject type that provides a seemingly strongly-typed façade for a IDataReader.
using System.Data;
using System.Dynamic; // needs assembly references to System.Core & Microsoft.CSharp
using System.Linq;
public static class DataReaderExtensions
{
public static dynamic AsDynamic(this IDataReader reader)
{
return new DynamicDataReader(reader);
}
private sealed class DynamicDataReader : DynamicObject
{
public DynamicDataReader(IDataReader reader)
{
this.reader = reader;
}
private readonly IDataReader reader;
// this method gets called for late-bound member (e.g. property) access
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
int index = reader.GetOrdinal(binder.Name);
result = index >= 0 ? reader.GetValue(index) : null;
return index >= 0;
}
}
}
Then you can use it like this:
using (IDataReader reader = someSqlCommand.ExecuteReader(…))
{
dynamic current = reader.AsDynamic(); // façade representing the current record
while (reader.Read())
{
// the magic will happen in the following two lines:
int id = current.Id; // = reader.GetInt32(reader.GetOrdinal("Id"))
string name = current.Name; // = reader.GetString(reader.GetOrdinal("Name"))
…
}
}
But beware, with this implementation, all you get is a façade for the current record. If you want to keep data of several records in memory, this implementation won't help a lot. For that purpose, you could look into several further possibilities:
Use anonymous objects: cachedRecords.Add(new { current.Id, current.Name });. This is only any good if you access the cachedRecords in the same method where you build it, because the anonymous type used will not be usable outside of the method.
Cache current's data in an ExpandoObject.
If you want to manually write a data type for each combination of columns resulting from your queries, then you have a lot of work to do, and you will end up with lots of very similar, but slightly different classes that are hard to name. Note also that these data types should not be treated as something more than what they are: Data Transfer Objects (DTOs). They are not real domain objects with domain-specific behaviour; they should just contain and transport data, nothing else.
What follows are two suggestions, or ideas. I will only scratch at the surface here and not go into too many details; since you haven't asked a very specific question, I won't provide a very specific answer.
1. A better approach might be to determine what domain entity types you've got (e.g. Person, Appointment) and what domain value types you have (e.g. Phone Number), and then build an object model from that:
struct PhoneNumber { … }
partial interface Person
{
int Id { get; }
string Name { get; }
PhoneNumber PhoneNumber { get; }
}
partial interface Appointment
{
DateTime Date { get; }
Person[] Participants { get; }
}
and then have your database code map to these. If, for example, some query returns a Person Id, Person Name, Phone Number, and an Appointment Date, then each attribute will have to be put into the correct entity type, and they will have to be linked together (e.g. via Participants) correctly. Quite a bit of work. Look into LINQ to SQL, Entity Framework, NHibernate or any other ORM if you don't want to do this manually. If your database model and your domain model are too different, even these tools might not be able to make the translation.
2. If you want to hand-code your data query layer that transforms data into a domain model, you might want to set up your queries in such a way that if they return one attribute A of entity X, and entity X has other attributes B, C, and D, then the query should also return these, such that you can always build a complete domain object from the query result. For example, if a query returned a Person Id and a Person Phone Number, but not the Person Name, you could not build Person objects (as defined above) from the query because the name is missing.
This second suggestion will at least partially save you from having to define lots of very similar DTO types (one per attribute combination). This way, you can have a DTO for a Person record, another for a Phone Number record, another for an Appointment record, perhaps (if needed) another for a combination of Person and Phone Number; but you won't need to distinguish between types such as PersonWithAllAttributes, PersonWithIdButWithoutNameOrPhoneNumber, PersonWithoutIdButWithPhoneNumber, etc. You'll just have Person containing all attributes.

How Do I Query Objects With Nested Properties Using NHibernate?

Okay, I've seen some similar questions to this, but the answers either confused me or seemed completely over-engineered, so I'd like to ask my own question.
I have a class called Tree, which has an object property from the class Plot, which has an object property from the class Year, which has an object property from the class Series, which has a string property called Id. This is summarized below.
public class Tree {
public virtual Plot Plot { get; set; }
// other properties...
}
public class Plot {
public virtual Year Year { get; set; }
// other properties...
}
public class Year {
public virtual Series Series { get; set; }
// other properties...
}
public class Series {
public virtual string Id { get; set; }
// other properties...
}
Each of these classes corresponds to the table of a database, and properties correspond to foreign key fields (for example, the Trees table has a field called PlotKey, which refers to a record in the Plots table). All I want to do is load all trees from the database whose corresponding Series have the Id "Adrian_2012" or "IPED Sample". I thought this would be a pretty easy taking using the following code:
IList<Tree> trees = session.CreateCriteria<Tree>()
.Add(Expression.Or(
Expression.Eq("Plot.Year.Series.Id", "Adrian_2012")
Expression.Eq("Plot.Year.Series.Id", "IPED Sample")
))
.List<Tree>();
But this is throwing: "NHibernate.Exceptions.GenericADOException : could not execute query". I have tried using Expression.Disjunction, I have tried using Aliases, Restrictions, and SimpleExpressions, and I know that nothing stupid like unmapped properties or misspelled criteria is occurring. The only other thing I've seen that might help is the ISession.QueryOver<>() function, but I get very confused by lambda expressions. Does anyone have a solution for me that would use just a simple CreateCriteria<> statement like that above?
Thanks in advance!
One not nice side of the Criteria queries is, that we have to define associations chain explicitly. I.e. we have to introduce JOIN:
15.4. Associations (cite:)
You may easily specify constraints upon related entities by navigating associations using CreateCriteria().
So to have a JOIN we need syntax like this
var trees = session
.CreateCriteria<Tree>()
.CreateCriteria("Plot", "p")
.CreateCriteria("Year", "y")
.CreateCriteria("Series", "s")
.Add(Expression.Or(
Expression.Eq("s.Id", "Adrian_2012")
Expression.Eq("s.Id", "IPED Sample")
))
.List<Tree>();
Also, check this:
NHibernate - CreateCriteria vs CreateAlias

Prevent EF 5 from generating a property

I'm using EF5 database first with partial classes. There's a property in my partial class which contains n object which is stored as a column in my database containing XML data. I want to handle the serialization/deserialization of this object when the EF tries to read/write it with a custom getter/setter.
Is it possible to expose the column in my partial class and map it using the EF, without auto-generating a property for it?
ie:
public SomeObject BigComplexObject { get; set; } // forms etc in my app use this
public string BigComplexObjectString // when the EF tries to read/write the column, my custom getter/setter kicks in
{
get { return this.BigComplexObject.ToXmlString(); }
set { this.BigComplexObject = new BigComplexObject(value); }
}
At present, the EF is auto-generating a member for the column so I'm left with two.
Try to change the logic. Leave EF generated property that will be populated with XML string from the database:
public string BigComplexObjectString { get; set; }
Then do the following:
[NotMapped]
public SomeObject BigComplexObject
{
get { return new SomeObject(this.BigComplexObjectString); }
set { this.BigComplexObjectString = value.ToXmlString(); }
}
Don't forget to add [NotMapped] to instruct EF to ignore this property.
Well, we use a little trick for a quite similar case...
We use the property panel (in the edmx file) of our... properties and add something in the "documentation" (summary or long description) line (probably not the best place, but anyway). This can be access by your T4 file.
So you could write something like "useXml" in the property panel, then modify your tt to generate the desired code when (example to get the info in the .tt file)
if (edmProperty.Documentation != null && edmProperty.Documentation.Summary = "useXml")
//generate something special
It would be great to have a better place for "cusom infos" in the edmx, but we didn't find anything better for instant.

Why is my DbModelBuilder configuration ignored when mapping Entity from DbSet<T>.SqlQuery?

I have a DbModel configuration like so:
modelBuilder.Entity<WishlistLine>()
.HasKey(w => w.PersistenceKey)
.Property(w => w.PersistenceKey)
.HasColumnName("WishlistLineId");
I have a query run via the following two methods:
public IEnumerable<WishlistLine> FetchWishlistLinesUsingLogonName(string logonName)
{
return GetFromRawSql(#"
SELECT wl.* FROM WishlistLines wl
INNER JOIN Accounts a ON wl.AccountId = a.AccountId
LEFT JOIN Users u ON u.AccountId = a.AccountId
WHERE u.LogonName = #p0", logonName);
}
protected IEnumerable<TEntity> GetFromRawSql(string sqlQuery, params object[] parameters)
{
return _dbSet.SqlQuery(sqlQuery, parameters).ToList();
}
I can "save" WishlistLines into the database through EF without any problems. When I run this query though I get this error:
The data reader is incompatible with the specified 'DataAccessLayer.DatabaseContext.WishlistLine'. A member of the type, 'PersistenceKey', does not have a corresponding column in the data reader with the same name.
I understood that using DbSet<T>.SqlQuery() would map the returned data to the entities but it seems to be ignoring the DbModel configurations. Judging (guessing) from the error message the wrong data reader is being used.
so:
A) am I doing anything wrong?
B) is there a way to make use of EF's DbModel-aware entity mapper?
Indeed the column name mapping is ignored when you execute a raw SQL query. Here are two references: This pretty dissatisfying thread only for fun, but the following one with a serious answer from the EF team:
Quote from http://entityframework.codeplex.com/workitem/233:
The SqlQuery method is designed not to take any mapping into account,
including mapping that is applied using attributes. It simply matches
the column names from the results with property names in the object.
If the column names don't match you will need to use a column alias
(AS keyword in SQL Server) to rename the column in the results.
We agree that it would be useful to have the option to make SqlQuery
honor Column attributes so we're keeping this issue open and putting
it on our backlog for future consideration.
So, the only workaround seems to be an explicit AS alias instead of a * in your SQL query that specifies your property name as a column alias:
return GetFromRawSql(#"
SELECT wl.WishlistLineId AS PersistenceKey,
wl.SomeOtherColumn AS SomeOtherProperty,
...
..."
// ...
I found another solution that is quite clean. In my model, I have public properties with the nice names I want to use, and private properties with the exact same name as in the database, and return the private value in the getter of the public ones, like this:
public class KeywordsStatistic
{
public string Keyword { get { return lib_motcle; } }
public int NumActions { get { return nbrActions; } }
private string lib_motcle { get; set; }
private int nbrActions { get; set; }
}
Of course, this would need to be modified if the values need to be updated, but the principles are the same...
HTH

Categories

Resources