Fill property of DTO with SubQuery in NHibernate Query - c#

I have a DTO object like this:
public class TreeViewDTO
{
public string Value { get; set; }
public string Text { get; set; }
public bool HasChildren { get; set; }
}
and my entity mapped with Nhibernate is:
public class Entity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Entity Parent { get; set; }
/* other properties */
}
I would like to know, how can I get a List of my DTOs and fill the HasChildren property using a count method or a subquery to know if there are childrens?
I have tried this, but does not work:
return Session.QueryOver<Entity>
.Select(entity => new TreeViewViewModel() {
Value = entity.Id.ToString(),
Text = entity.Name,
HasChildren = (Session.QueryOver<Entity>().Where(x => x.ParentId == entity.Id).RowCount() > 0)})
.ToList();
I got an exception with this: NotSupportedException and the messages says: x => (x.Parent.Id == [100001].Id) and it is not supported.
How could I create a query to fill this property?
PS: I would like to have a query to select only the Id, Name and Count... because my entity can have 30 fields or more...
Thank you.

Using the NHibernate Linq provider then you can do this:-
public class dto
{
public long Value { get; set; }
public int Count { get; set; }
public bool HasCount { get { return Count > 0; } }
}
Note: my DTO has a read-only property that looks at the actual count, the query is then:-
var a = Db.Session.Query<Support>().Select(
s => new dto {
Value = s.Id,
Count = s.CommentList.Count
}
).ToList();
This generates the following sQL
select support0_.Id as col_0_0_,
(select cast(count(*) as SIGNED)
from supportcomment commentlis1_
where support0_.Id = commentlis1_.SupportId) as col_1_0_
from support support0_
I have never seen a working example of this using QueryOver. I have had had a stab at it but couldn't get it working..

Didn't you consider the option of using something else rather than NHibernate for this job?
In my opinion, lightweight library like Dapper can be a brilliant solution for this use case. You'll end up with a resonably simple sql query instead of jiggling with Nhibernate.
Edit:
dapper code will be as simple as this:
public IDbConnection ConnectionCreate()
{
IDbConnection dbConnection = new SQLiteConnection("Data Source=:memory:;pooling = true;");
dbConnection.Open();
return dbConnection;
}
public void Select()
{
using (IDbConnection dbConnection = ConnectionCreate())
{
var query = #"SELECT e1.id as Value, e1.name as Text, CASE WHEN EXISTS
(SELECT TOP 1 1 FROM Entity e2 WHERE e2.parent = e1.id)
THEN 1 ELSE 0 END as HasChildren
FROM Entity e1";
var productDto = dbConnection.Query<TreeViewDTO>(query);
}
}

Related

Sub-Query or Join with embedded/nested document using C# LINQ with MongoDB

I am trying to do something like bellow example but getting exception as -
System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable1 ' cannot be used for parameter of type 'System.Linq.IQueryable1' of method.
Here is my code and related classes . how can i resolve this issue, is there any way except which a trying to do.
var channels = _channelService.Collection;
var tracks = _trackService.Collection;
var query = from b in tracks.AsQueryable()
select b;
var data = (from q in channels.AsQueryable()
from p in q.Episodes
//from x in trackcoll.AsQueryable()
select new
{
p,
Tracks = query.Where(w => p.Tracks.Contains(w.Id))
}).ToList();
// Related classes
public class ContentBase : IAuditable
{
public string Id { get; set ; }
public string CreatedBy { get ; set ; }
public string CreatedOn { get ; set ; }
public string UpdatedBy { get ; set ; }
public string UpdatedOn { get; set; }
}
public class Channel: ContentBase
{
public List<Episode> Episodes { get; set; }
}
public class Episode: ContentBase
{
// List of track Id
public List<string> Tracks { get; set; }
}
public class Track: ContentBase
{
public string TrackUrl { get; set; }
public string Duration { get; set; }
public string Size { get; set; }
public string ContentType { get; set;
}
MongoDB's LINQ support for joins is limited to equal joins as described here. Your expression cannot be translated into Aggregation Framework's $lookup since there's no equivalent syntax for .Contains().
Therefore you have to run an operation that's closer to Aggregation Framework syntax. One example is a fluent aggregate interface which allows you to run extension methods having the same name as Aggregation Framework's operators. Try:
var q = _channels.Aggregate()
.Unwind(x => x.Episodes)
.Lookup(
foreignCollectionName:"tracks",
localField:"Episodes.Tracks",
foreignField:"_id",
#as:"Tracks");
var result = q.ToList();
Above code will return a List<BsonDocument>
mickl's answer will get you there with the official driver, but if you don't like dealing with bsondocuments and would like to have some degree of type-safety, you can simply do the following with mongodb.entities library (which i'm the author of):
public class EpisodeWithTracks
{
public Track[] Tracks { get; set; }
}
var pipeline = new Template<Channel, EpisodeWithTracks>(#"
[
{
$unwind: '$<Episodes>'
},
{
$lookup: {
from: '<track_collection>',
localField: '<Episodes.Tracks>',
foreignField: '_id',
as: '<Tracks>'
}
}
]")
.Path(c => c.Episodes)
.Tag("track_collection", collectionName)
.Path(c => c.Episodes[0].Tracks)
.PathOfResult(ewt => ewt.Tracks);
var result = DB.Aggregate(pipeline)
.ToList();
here's the wiki page explaining how it works.

Check value of same column in all tables [Entity Framework]

I have more than 100 tables in data base in which 60+ table's contain column called ShortCode nvarchar(12) which represent globally unique code of that record.
Now is there any way to find that the ShortCode value eg. AST_SHIP_FIRE present in any of the table in database.
Note:ShortCode is user define.
currently I am try below code,it works but I have to code for all table.
if (entities.Table1.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower())
{return false;}
else if(entities.Table2.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower()))
{return false;}
else if( entities.Talble3.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower()))
{return false;}
.
.
.
else
{
//insert code
}
I think there may be more efficient way.
Ok, maybe not very straightforward but lets do it!
First of all define an interface for ShortCode property and implement it by any entity that has it:
public interface ITableWithShortCode
{
public string ShortCode { get; set; }
}
public class Table1 : ITableWithShortCode
{
public long Id { get; set; }
public string ShortCode { get; set; }
}
public class Table2 : ITableWithShortCode
{
public long Id { get; set; }
public string ShortCode { get; set; }
}
Now using power of Reflection you can write a method like this:
public bool IsExistShortCode(string shortCode)
{
using (var context = new AppDbContext())
{
/*
find all tables that are defined in your DbContext and are implemented ITableWithShortCode like:
public DbSet<Table1> Table1 { get; set; }
public DbSet<Table2> Table2 { get; set; }
...
*/
var properties = typeof(AppDbContext).GetProperties()
.Where(p => p.PropertyType.IsGenericType
&& typeof(ITableWithShortCode).IsAssignableFrom(p.PropertyType.GenericTypeArguments[0]));
foreach (var property in properties)
{
var contextProp = (IQueryable<ITableWithShortCode>)property.GetValue(context);
bool isExist = contextProp.Any(p => p.ShortCode == shortCode);
if (isExist)
return true;
}
return false;
}
}
Note: You can do some optimization on this code, I prefered to keep it in its simplest state to show the idea. But in production, for example you can easily cache DbContext properties on startup and use it afterward

Using Lambda to insert derived attribute into IQueryable dataset

I have the following query:
IQueryable<BarcodeQuery> barcodes = db.Barcodes.Select(b => new BarcodeQuery
{
id = b.id,
category_id = b.category_id,
...
checkout = b.Checkouts.Select(c => new CheckoutChild
{
id = c.id,
loanee_id = c.loanee_id,
...
})
.Where(c => c.datein == null)
.FirstOrDefault()
});
And so on. It's based on this model:
public class BarcodeQuery
{
public int id { get; set; }
public int category_id { get; set; }
...
public CheckoutChild checkout { get; set; }
public CheckoutStatus checkoutStatus { get; set; }
}
My question is about CheckoutStatus down there at the bottom. It looks like this:
public class CheckoutStatus
{
public string status { get; set; }
public int daysUntilDue { get; set; }
public int daysOverdue { get; set; }
}
All of those values are derived from information I get from the query--none of them are in the database itself. What is the best way of inserting the CheckoutStatus values into each barcode record?
I have a function that creates the CheckoutStatus values themselves, I just don't know how to get them into the barcode records.
Thanks!
If b has just be created with new, how can b.Checkouts contain something? I do not really understadn what you are trying to do.
EF is converting the lambda expression into a SQL statement. Therefore you can only use expressions that can actually be translated to SQL. Just query the barcodes from the DB and then add the missing information to the barcodes returned in a loop.
var barcodes = db.Barcodes.Select(...).ToList();
foreach (Barcode b in barcodes) {
b.Checkouts = ...
}

linq/ef: How to form the query which connects multiple tables to return result?

I'm extremely new to ASP .NET and LINQ so please forgive me for my ignorance.
I've a Region class:
public class Region
{
[Key]
public int Region_ID { get; set; }
public string Region_Name { get; set; }
}
And a Service class:
public class Service
{
[Key]
public int Service_ID { get; set; }
public string Service_Name { get; set; }
}
And a mapping class which stores the many-many mapping of service_IDs with region_IDs:
public class Mapping_ServiceToRegion
{
[Key]
public int Service_ID { get; set; }
public int Region_ID { get; set; }
}
Now I want to create an API function which outputs Region_Name based on given Service_ID. This is what I have so far in my RegionsController:
// GET api/Regions/Service_ID
[ResponseType(typeof(Region))]
public async Task<IHttpActionResult> GetRegion(int id)
{
var region_id = from sr in db.Mapping_ServiceToRegions
where sr.Service_ID == id
select sr.Region_ID;
var region = await db.Regions.Select(r =>
new Region()
{
Region_ID = r.Region_ID,
Region_Name = r.Region_Name
}).SingleOrDefaultAsync(r => r.Region_ID == region_id); //ERROR
if (region == null)
{
return NotFound();
}
return Ok(region);
}
The error I'm getting is:
Cannot convert lambda expression because it is not a delegate type.
I realize that my region_id variable will have multiple region_ids based on a service_id. How can I modify the code to account for this? Is there an IN operator that I can use to say r.Region_ID IN region_id?
And does the above code look correct otherwise?
Thanks.
You should change the SingleOrDefaultAsync() call using Contains() method like below since your region_id is of IEnumerable<T> and not a single value and so you can't perform direct equality comparison.
SingleOrDefaultAsync(r => region_id.Contains(r.Region_ID))
Ahh!!! here Region is one of EF mapped entity and you are trying to construct that and thus the error. You should either chose to select an Anonymous type (or) use a custom viewmodel/DTO object like
var region = await db.Regions.Select(r =>
new
{
Region_ID = r.Region_ID,
Region_Name = r.Region_Name
}).SingleOrDefaultAsync(r => region_id.Contains(r.Region_ID));

Mapping Dapper Query to a Collection of Objects (which itself has a couple of Collections)

I want to execute a single Query (or Stored Proc with multiple resultsets). I know how to do Multi-mapping using Dapper, but I can't sort how to map the two collections onto the same parent. Basically, given this Object definition...
class ParentObject
{
string Name { get; set; }
ICollection<ChildObjectOne> ChildSetOne {get;set;}
ICollection<ChildObjectTwo> ChildSetTwo { get; set; }
}
class ChildObjectOne
{
string Name { get; set; }
}
class ChildObjectTwo
{
int id { get; set; }
string LocationName { get; set; }
}
I want to be able to run a Dapper query that somehow yields:
IQueryable<ParentObject> result = cnn.Query(
// Some really awesome dapper syntax goes here
);
Not sure if you DON'T want to use MultiMapping but here's how it would work for your case. As far as I know and read on SO, is not not possible to map a deep nested object graph with a simple Query.
static void Main(string[] args)
{
var sqlParent = "SELECT parentId as Id FROM ParentTable WHERE parentId=1;";
var sqlChildOneSet = "SELECT Name FROM ChildOneTable;"; // Add an appropriate WHERE
var sqlChildTwoSet = "SELECT Id, LocationName FROM ChildTwoTable;"; // Add an appropriate WHERE
var conn = GetConnection() // whatever you're getting connections with
using (conn)
{
conn.Open();
using (var multi = conn.QueryMultiple(sqlParent + sqlChildOneSet + sqlChildTwoSet))
{
var parent = multi.Read<ParentObject>().First();
parent.ChildSetOne = multi.Read<ChildOne>().ToList();
parent.ChildSetTwo = multi.Read<ChildTwo>().ToList();
}
}
}
Similar questions for nested objects and dapper :
https://stackoverflow.com/search?q=nested+objects+%2B+dapper
It is possible to materialize an object with one-to-many relationships using the IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map); method in this case. However you need to make a few changes to the entities in order to have enough information to do so.
Here are a few SO threads with similar questions.
How do I map lists of nested objects with Dapper
Extension function to make it cleaner
Dapper.Net by example - Mapping Relationships
public class ParentObject
{
public ParentObject()
{
ChildSetOne = new List<ChildObjectOne>();
ChildSetTwo = new List<ChildObjectTwo>();
}
// 1) Although its possible to do this without this Id property, For sanity it is advisable.
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ChildObjectOne> ChildSetOne {get; private set;}
public ICollection<ChildObjectTwo> ChildSetTwo { get; private set; }
}
public class ChildObjectOne
{
// 2a) Need a ParentId
public int ParentId { get; set; }
public string Name { get; set; }
}
public class ChildObjectTwo
{
// 2b) This ParentId is not required but again for sanity it is advisable to include it.
public int ParentId { get; set; }
public int id { get; set; }
public string LocationName { get; set; }
}
public class Repository
{
public IEnumerable<ParentObject> Get()
{
string sql =
#"SELECT
p.Id,
p.Name,
o.Name,
o.ParentId,
t.Id,
t.LocationName,
t.ParentId
FROM
Parent p
LEFT JOIN ChildOne o on o.ParentId = p.Id
LEFT JOIN ChildTwo t on t.ParentId = p.Id
WHERE
p.Name LIKE '%Something%'";
var lookup = new Dictionary<int, ParentObject>();
using (var connection = CreateConnection())
{
connection.Query<ParentObject, ChildObjectOne, ChildObjectTwo, ParentObject>(
sql, (parent, childOne, childTwo) =>
{
ParentObject activeParent;
if (!lookup.TryGetValue(childOne.ParentId, out activeParent))
{
activeParent = parent;
lookup.add(activeParent.Id, activeParent);
}
//TODO: if you need to check for duplicates or null do so here
activeParent.ChildSetOne.Add(childOne);
//TODO: if you need to check for duplicates or null do so here
activeParent.ChildSetTwo.Add(childTwo);
});
}
return lookup.Values;
}
}

Categories