Nhibernate get rows grouped by column - c#

In short i want the following grouping functionality: http://demos.telerik.com/aspnet-mvc/grid/index
However, I cannot seem to figure out how to enable my back end to retrieve this data dynamically.
I have the following line:
query.UnderlyingCriteria.SetProjection(Projections.GroupProperty("PropertyNumber"));
Where query is of type IQueryOver.
However, when I use
var statistics = query.List<T>();
I get
"The value \"000000\" is not of type \"SDS" and cannot be used in this generic collection.\r\nParameter name: value"}
000000 is a default property number, and SDS is the object I'm attempting to group. It is obvious to me that what I've written is attempting to cast strings back as the value.
Interestingly the following returns the exact count of rows in the table.
var rowCount = query.RowCount();
My question then, is how do I actually return SDS in a grouped manner, if the projection cannot do what I want?

There are two issues in general:
once we've used projection, any other property which we want to get must be projected as well.
if there is a projection and we need to get a list of entities, we need result transformer
Having that we can get the list like this:
// the As() is essential for result transformer
query
.UnderlyingCriteria
.SetProjection(Projections.GroupProperty("PropertyNumber")
.As("PropertyNumber"));
// set Transformer
query.TransformUsing(Transformers.AliasToBean<T>());
// the list of T, but only PropertyNumber is filled by NHibernate
var list = query.List<T>();
or this to get more properties filled
query
.UnderlyingCriteria
.SetProjection(
Projections.ProjectionList()
.Add(Projections.GroupProperty("PropertyNumber").As("PropertyNumber"))
.Add(Projections.Count("ID").As("Count"))
....
)
;
// set Transformer to some DTO
query.TransformUsing(Transformers.AliasToBean<U>());
// the list of DTO, with more than PropertyNumber filled
var list = query.List<U>(); // generic U representing DTO
Finally, the QueryOver API has its own way how to declare projections
16.6. Projections

Related

How to Update/Merge two huge List<Class> with hundreds of properties in C#, based on common matching Key in most efficient way

I have two large set of collection List<Class> with hundreds of properties.
e.g. Original Collection List<OriginalCollection> and Updated Collection List<UpdatedColleciton>
UpdatedCollection will contain value in certain columns which most probably will not be part of OriginalCollection and UpdatedCollection might have certain KeyColumn [ID Column] which might not be part of OriginalCollection, and I'm receiving thousands of data set in OriginalCollection and UpdatedColletion will increase in records over a period of time.
I do have a requirement where only null or empty column of OriginalCollection should get replaced with matching UpdatedCollection value by ID and if no matching ID is available then those records should get added in OriginalCollection from UpdatedCollection.
I tried with AutoMapper, where I tried to Update OriginalCollection with UpdatedCollection based on matching ID, for which I'm unable to find any AutoMapper configuration for my sets of above mentioned requirement.
I'm looking for most effective solution which should not impact on performance, thats why I did not go threw typical way of Union and Intersection, as Modal have hundreds of property and thousands of records are there, and as I do have plenty of properties I think library like AutoMapper would be good option than writing logic in loop to check value on each column for all thousands of record.
Please suggest any better and performance efficient solution like AutoMapper Configuration or any other .Net inbuilt feature to achieve this scenario.
I also checked with AutoMapper.Collection as below from https://github.com/AutoMapper/AutoMapper.Collection
cfg.CreateMap<OrderItemDTO, OrderItem>().EqualityComparison((odto, o) => odto.ID == o.ID);
Mapping OrderDTO back to Order will compare Order items list based on if their ID's match
Mapper.Map<List<OrderDTO>,List<Order>>(orderDtos, orders);
but it has below behavior and not working as expected to my requirement
If ID's match will map OrderDTO to Order
If OrderDTO exists and Order doesn't add to collection Not Working for me
If Order exists and OrderDTO doesn't remove from collection Not Working for me
AutoMapper is known mapping library and has good documentation as well, but unable to find similar detaild documentation for AutoMapper.Collection, I've explored AutoMapper.Collection but it was not providing solution as per my requirement.
So, I need to go traditional way.
Prepared differences of collection by LINQ query.
var common = original.Where(x => revised.Exists(z => z.ID == x.ID)).ToList();
var nonCommon = revised.Where(x => !original.Exists(z => z.ID == x.ID)).ToList();
foreach(var item in common)
{
var derived = revised.FirstOrDefault(x => x.ID == item.ID);
// Added Extention Method to compare and update property
var data = item.UpdateProperties(derived);
}
common.AddRange(nonCommon);
Utilized Reflection to compare objects and update it's value after comparision on property level, for all datatypes.
public static T UpdateProperties<T>(this T source, T destination)
{
Type type = source.GetType(); // Gets Source Object Type
PropertyInfo[] props = type.GetProperties(); // Gets Source object Properties
foreach (var prop in props) // Iterate threw all properties of source object
{
var sourceValue = prop.GetValue(source); // Get source object value by Property Name
var destinationValue = prop.GetValue(destination); // Get destination object value by Property Name, to update source object
// Update source object property value only if derived object's property has value and source object doesn't
if (string.IsNullOrEmpty(sourceValue?.ToString()) && !string.IsNullOrEmpty(destinationValue?.ToString()))
{
prop.SetValue(source, destinationValue); // Update source object's property with value of derived object
}
}
return source;
}

Where clause in Linq in List c#

I have a struct like this:
struct Test
{
string name;
string family;
public Test...
}
in my code I have a list of this struct:
List<Test> testList=new List<Test>();
I have a linq expression like this:
var list =testList.Select(n=>n.Name);
but how can I filter this selection by Family of the testList? something like this:
var list=testList.Select(n=>n.Name).Where(f=>f.Family=="");
this Where clause just apply on selected Names which are a list of string
Any ideas how to do that?
Just put the Where before the Select:
var list=testList.Where(f=>f.Family=="").Select(n=>n.Name);
In Linq you need to apply the filter before projecting (unless the filter applies to the results of the projection rather than the original collection).
Filter using Where before you select just one property using Select. That way, you still get the full object:
testList.Where(t => t.Family == "").Select(t => t.Name)
After all, Select will take the object and then only pass on whatever you return in the lambda. In this case, you only return a string, so you throw all the other information from the Test object away. And as such, the information you want to filter on isn’t available anymore.
If you switch that around, you can filter on the test object, and then return only that one string.
Normally when I am checking for an empty value like that, I'll use string.IsNullOrEmpty(), just in case.
testList.Where(f=> string.IsNullOrEmpty(f.Family)).Select(n=>n.Name);
You should first apply where clause and the select your desired data:
var list=testList.Where(f=>f.Family=="").Select(n=>n.Name);

NHibernate doesn't map query result to its type

I was wondering if it possible to retrieve the pure result of the query and not its mapped result. keeping in mind that my entity type is only know at runtime.
var session = _sessionProvider.GetSession();
return session.QueryOver<object>(type.FullName).List()
This query will return a list mapped to its concrete type. But i would like to retrieve an IEnumerable<object[]> which contains for example [0] { Id : 1, RoleId : 1, Name : "Name" }.
So you want to transform something into something. I expect that you know at least the column names you want to have in your output.
You could do this for example by using the PassThrough result transformer and define the projections with Projections.Property("<colName>")
object alias = null;
var result = session.QueryOver<object>(type.FullName)
.SelectList(list => list
.Select(Projections.Id())
.Select(Projections.Property("RoleId"))
.Select(Projections.Property("Name")))
.TransformUsing(Transformers.PassThrough)
.List<object[]>();
If you do not know the properties you could query your type with reflection of what not...

Is there any way of knowing the content of an IQueryable object without using Reflection?

I have to work with a given class "QueryGenerator" that generates dynamic queries from selected Tables and Columns by the user using a StringConnection and a Provider. Anyways, i don't have to know the implementation of the class but i have to use it and i'm stock.
At the end, the "QueryGenerator" returns the result query as an object, the only thing that i know (because i use Reflector on the class) is that i can do an IQueryable cast on that query result. Here is an example:
var result = (IQueryable)myQueryGenerator.Result;
And for knowing the content of result, i have to use Reflection.
So, is there any better way of finding out the content of result , and, for example, fill a DataSet with it?
No, there isn't.
IQueryable could return objects of different types (e.g. if you query against an array of objects). In this extreme case you would need to determine the type for each individual item in the enumerated query as you access them.
Example code to demonstrate this scenario:
object[] objs = new object[3]{ "string", 78, DateTime.Now };
var q = objs.AsQueryable().Skip(1).Take(2);
foreach( var o in q )
{
var t = o.GetType();
}

Retrieve an object from entityframework without ONE field

I'm using entity framework to connect with the database. I've one little problem:
I've one table which have one varbinary(MAX) column(with filestream).
I'm using SQL request to manage the "Data" part, but EF for the rest(metadata of the file).
I've one code which has to get all files id, filename, guid, modification date, ... of a file. This doesn't need at all the "Data" field.
Is there a way to retrieve a List but without this column filled?
Something like
context.Files.Where(f=>f.xyz).Exclude(f=>f.Data).ToList();
??
I know I can create anonymous objects, but I need to transmit the result to a method, so no anonymous methods. And I don't want to put this in a list of anonymous type, and then create a list of my non-anonymous type(File).
The goal is to avoid this:
using(RsSolutionsEntities context = new RsSolutionsEntities())
{
var file = context.Files
.Where(f => f.Id == idFile)
.Select(f => new {
f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
f.DateModification, f.FileId
}).FirstOrDefault();
return new File() {
DataType = file.DataType, DateModification = file.DateModification,
FileId = file.FileId, FileName = file.FileName, Id = file.Id,
MimeType = file.MimeType, Size = file.Size
};
}
(I'm using here the anonymous type because otherwise you will get a NotSupportedException: The entity or complex type 'ProjectName.File' cannot be constructed in a LINQ to Entities query.)
(e.g. this code throw the previous exception:
File file2 = context.Files.Where(f => f.Id == idFile)
.Select(f => new File() {Id = f.Id, DataType = f.DataType}).FirstOrDefault();
and "File" is the type I get with a context.Files.ToList(). This is the good class:
using File = MyProjectNamespace.Common.Data.DataModel.File;
File is a known class of my EF datacontext:
public ObjectSet<File> Files
{
get { return _files ?? (_files = CreateObjectSet<File>("Files")); }
}
private ObjectSet<File> _files;
Is there a way to retrieve a List but without this column filled?
Not without projection which you want to avoid. If the column is mapped it is natural part of your entity. Entity without this column is not complete - it is different data set = projection.
I'm using here the anonymous type because otherwise you will get a
NotSupportedException: The entity or complex type 'ProjectName.File'
cannot be constructed in a LINQ to Entities query.
As exception says you cannot project to mapped entity. I mentioned reason above - projection make different data set and EF don't like "partial entities".
Error 16 Error 3023: Problem in mapping fragments starting at line
2717:Column Files.Data in table Files must be mapped: It has no
default value and is not nullable.
It is not enough to delete property from designer. You must open EDMX as XML and delete column from SSDL as well which will make your model very fragile (each update from database will put your column back). If you don't want to map the column you should use database view without the column and map the view instead of the table but you will not be able to insert data.
As a workaround to all your problems use table splitting and separate the problematic binary column to another entity with 1 : 1 relation to your main File entity.
I'd do something like this:
var result = from thing in dbContext.Things
select new Thing {
PropertyA = thing.PropertyA,
Another = thing.Another
// and so on, skipping the VarBinary(MAX) property
};
Where Thing is your entity that EF knows how to materialize. The resulting SQL statement shouldn't include the large column in its result set, since it's not needed in the query.
EDIT: From your edits, you get the error NotSupportedException: The entity or complex type 'ProjectName.File' cannot be constructed in a LINQ to Entities query. because you haven't mapped that class as an entity. You can't include objects in LINQ to Entities queries that EF doesn't know about and expect it to generate appropriate SQL statements.
You can map another type that excludes the VarBinary(MAX) column in its definition or use the code above.
you can do this:
var files = dbContext.Database.SqlQuery<File>("select FileId, DataType, MimeType from Files");
or this:
var files = objectContext.ExecuteStoreQuery<File>("select FileId, DataType, MimeType from Files");
depending on your version of EF
I had this requirement because I have a Document entity which has a Content field with the content of the file, i.e. some 100MB in size, and I have a Search function that I wanted to return the rest of the columns.
I chose to use projection:
IQueryable<Document> results = dbContext.Documents.Include(o => o.UploadedBy).Select(o => new {
Content = (string)null,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
FileName = o.FileName,
Id = o.Id,
// etc. even with related entities here like:
UploadedBy = o.UploadedBy
});
Then my WebApi controller passes this results object to a common Pagination function, which applies a .Skip, .Take and a .ToList.
This means that when the query gets executed, it doesn't access the Content column, so the 100MB data is not being touched, and the query is as fast as you'd want/expect it to be.
Next, I cast it back to my DTO class, which in this case is pretty much exactly the same as the entity class, so this might not be a step you need to implement, but it's follows my typical WebApi coding pattern, so:
var dtos = paginated.Select(o => new DocumentDTO
{
Content = o.Content,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
FileName = o.FileName,
Id = o.Id,
UploadedBy = o.UploadedBy == null ? null : ModelFactory.Create(o.UploadedBy)
});
Then I return the DTO list:
return Ok(dtos);
So it uses projection, which might not fit the original poster's requirements, but if you're using DTO classes, you're converting anyway. You could just as easily do the following to return them as your actual entities:
var dtos = paginated.Select(o => new Document
{
Content = o.Content,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
//...
Just a few extra steps but this is working nicely for me.
For EF Core 2
I implemented a solution like this:
var files = context.Files.AsNoTracking()
.IgnoreProperty(f => f.Report)
.ToList();
The base idea is to turn for example this query:
SELECT [f].[Id], [f].[Report], [f].[CreationDate]
FROM [File] AS [f]
into this:
SELECT [f].[Id], '' as [Report], [f].[CreationDate]
FROM [File] AS [f]
you can see the full source code in here:
https://github.com/aspnet/EntityFrameworkCore/issues/1387#issuecomment-495630292
I'd like to share my attempts to workaround this problem in case somebody else is in the same situation.
I started with what Jeremy Danyow suggested, which to me is the less painful option.
// You need to include all fields in the query, just make null the ones you don't want.
var results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName");
In my case, I needed a IQueryable<> result object so I added AsQueryable() at the end. This of course let me add calls to .Where, .Take, and the other commands we all know, and they worked fine. But there's a caveat:
The normal code (basically context.myEntity.AsQueryable()) returned a System.Data.Entity.DbSet<Data.DataModel.myEntity>, while this approach returned System.Linq.EnumerableQuery<Data.DataModel.myEntity>.
Apparently this means that my custom query gets executed "as is" as soon as needed and the filtering I added later is done afterwards and not in the database.
Therefore I tried to mimic Entity Framework's object by using the exact query EF creates, even with those [Extent1] aliases, but it didn't work. When analyzing the resulting object, its query ended like
FROM [dbo].[TableName] AS [Extent1].Where(c => ...
instead of the expected
FROM [dbo].[TableName] AS [Extent1] WHERE ([Extent1]...
Anyway, this works, and as long as the table is not huge, this method will be fast enough. Otherwise you have no option than to manually add the conditions by concatenating strings, like classic dynamic SQL. A very basic example in case you don't know what I'm talking about:
string query = "SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName";
if (parameterId.HasValue)
query += " WHERE Field1 = " + parameterId.Value.ToString();
var results = context.Database.SqlQuery<myEntity>(query);
In case your method sometimes needs this field, you can add a bool parameter and then do something like this:
IQueryable<myEntity> results;
if (excludeBigData)
results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName").AsQueryable();
else
results = context.myEntity.AsQueryable();
If anyone manages to make the Linq extensions work properly like if it was the original EF object, please comment so I can update the answer.
I'm using here the anonymous type because otherwise you will get a
NotSupportedException: The entity or complex type 'ProjectName.File'
cannot be constructed in a LINQ to Entities query.
var file = context.Files
.Where(f => f.Id == idFile)
.FirstOrDefault() // You need to exeucte the query if you want to reuse the type
.Select(f => new {
f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
f.DateModification, f.FileId
}).FirstOrDefault();
And also its not a bad practice to de-normalize the table into further, i.e one with metadata and one with payload to avoid projection. Projection would work, the only issue is, need to edit any time a new column is added to the table.
I tried this:
From the edmx diagram (EF 6), I clicked the column I wanted to hide from EF and on their properties you can set their getter and setter to private. That way, for me it works.
I return some data which includes a User reference, so I wanted to hide the Password field even though it's encrypted and salted, I just didn't want it on my json, and I didn't want to do a:
Select(col => new {})
because that's a pain to create and maintain, especially for big tables with a lot of relationships.
The downside with this method is that if you ever regenerate your model, you would need to modify their getter and setter again.
Using Entity Framework Power Tools you can do the following in efpt.config.json:
"Tables": [
{
"ExcludedColumns": [
"FileData"
],
"Name": "[dbo].[Attachment]",
"ObjectType": 0
}
]

Categories

Resources