NHibernate QueryOver and selecting rows where a propery is an ILIST and the list count >= 1 - nhibernate

I would like to select rows for which the property Items which is an IList has rows itself. In SQL its easy count >= 1 but in NHibernate it eludes me.
Tried many ways
public class Sale
{
private IList<Items> _items;
public Sale()
{
_items = new List<Item>();
}
public Guid SaleId { get; set; }
public string SaleNumber { get; set; }
public string Location { get; set; }
public DateTime? SaleDateTime { get; set; }
public IList<Item> Items => _items;
}
public class Item
{
public Guid ItemId { get; set; }
public string description { get; set; }
}
var testdata = _session.QueryOver<Sale>()
.Where(Restrictions.Ge(
Projections.Property<Sale>
(m => m.Items.Count), 1))
.ReadOnly()
.ListAsync();
Count is unrecognized

Technically if you did an inner join you wouldn't receive any Sale records back that do not have any Item records.
So as a really simple QueryOver you could do:
var sales = session.QueryOver<Sale>()
.Inner.JoinQueryOver(x => x.Items)
.Select(Projections.RootEntity())
.TransformUsing(Transformers.DistinctRootEntity)
.List();
This is the resulting SQL:
SELECT this_.SaleId as saleid1_1_0_,
this_.SaleNumber as salenumber2_1_0_,
this_.Location as location3_1_0_,
this_.SaleDateTime as saledatetime4_1_0_
FROM Sale this_
inner join Item item1_ on this_.SaleId=item1_.SaleId

Related

WindowsForms-EFCore SQL Exception Invalid Column Name

I have a two table products and categories. When I add a product to products, I get an error. I share the codes.
class Products
{
public int Id { get; set; }
public string Name { get; set; }
public float Price { get; set; }
public string Description { get; set; }
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public Categories Category { get; set; }
}
With this method, I get the products. After I fill a datagridview. But I want to see categoryName instead of CategoryId. It works, I see the categoryName instead of CategoryId in datagridview.
public List<Products> GetProducts()
{
var products = context.Products.Include(x =>x.Category ).Select(m => new Products()
{
Id = m.Id,
Name = m.Name,
Price = m.Price,
Description = m.Description,
CategoryName=m.Category.Name
}).ToList();
return products;
}
After that I have an Add method
public void AddProduct(Products products )
{
context.Products.Add(products);
context.SaveChanges();
}
However, when I try to add a new product, I have an error.
The issue is that Category Name is not in the physical table, just your object. So when EF attempts to generate the SQL, it can't find a column called CategoryName.
Take a look at this question
Exclude Property on Update in Entity Framework

LoadDocument by query in RavenDB index

I have the following structure:
public class Order
{
public string Id { get; set; }
public string CustomerId { get; set; }
public decimal Amount { get; set; }
public DateTimeOffset CreatedDate { get; set; }
}
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
}
I want to export all customers (RavenDB Stream) with order turnover and last ordered date.
I do already have an index (Customers_ByTurnover) which outputs this data (map=Orders, reduce by CustomerId). Although this does only list customers which have already ordered something.
I need an index for all Customers and load these details into each row.
Here is the code I want to write (the Query method is pseudo and doesn't really exist):
public class Customers_ByOrders : AbstractIndexCreationTask<Customer, Customers_ByOrders.Result>
{
public class Result
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Turnover { get; set; }
public DateTimeOffset? LastOrderedDate { get; set; }
}
public Customers_ByOrders()
{
Map = items => items.Select(item => new Result()
{
Id = item.Id,
Name = item.Name,
Turnover = Query<Order>().Where(x => x.CustomerId == item.Id).Sum(x => x.Amount),
LastOrderedDate = Query<Order>().Where(x => x.CustomerId == item.Id).Select(x => x.CreatedDate).OrderByDescending(x => x).FirstOrDefault()
});
}
}
How can I solve this issue?
You cannot create a query inside an index, to get the desired info you will have to create a map-reduce index on the Orders collection, group by on CustomerId and in the reduce function apply Sum() on Amount field and order the LastOrderedDate. To get the Name name field you will have to use LoadDocument extension.
public class Customers_ByOrders : AbstractIndexCreationTask<Orders, Customers_ByOrders.Result>
{
public class Result
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Turnover { get; set; }
public DateTimeOffset? LastOrderedDate { get; set; }
}
public Customers_ByOrders()
{
Map = orders => from o in orders
select new Result
{
Id = o.CustomerId,
Turnover = o.Amount,
LastOrderedDate = o.CreatedDate
};
Reduce = results => from result in results
group result by result.Id
into g
select new Result
{
Id = g.Key,
Turnover = g.Sum(x => x.Turnover),
LastOrderedDate = g.OrderByDescending(x => x.LastOrderedDate).Select(x => x.LastOrderedDate).FirstOrDefault()
};
}
}

Ravendb TransformResults showing null values for properties populated with Load()

I have two documents Ticket and MenuItem i have created index with TransformResults but problem is i am getting null value for Loaded document in transform
public class Ticket
{
public int ID { get; set; }
public int ItemId { get; set; }
public string ItemName { get; set; }
public int Price { get; set; }
}
public class MenuItem
{
public int ID { get; set; }
public string ItemName { get; set; }
public string PriceCategory { get; set; }
}
i have created a index like
public class TicketItemGross : AbstractIndexCreationTask<Ticket, TicketItemGross.TicketItemDetails>
{
public class TicketItemDetails
{
public string ID { get; set; }
public string ItemId { get; set; }
public string ItemName { get; set; }
public int Price { get; set; }
public string PriceCategory { get; set; }
}
public TicketItemGross()
{
Map = docs => from doc in docs
select new
{
ID = doc.ID,
ItemId=doc.ItemId,
ItemName=doc.ItemName,
Price=doc.Price
};
TransformResults = (database, docs) => from m in docs
let d = database.Load<MenuItem>(m.ID)
select new
{
ID = m.ID,
ItemId = m.ItemId,
ItemName = m.ItemName,
Price = m.Price,
PriceCategory=d.PriceCategory
};
}
}
and the problem is that when i query data. I get null for PriceCategory but for all other fields i get correct value
here is query
IEnumerable<TicketItemGross.TicketItemDetails> list;
using (var session = store.OpenSession())
{
list = session.Query<TicketItemGross.TicketItemDetails, TicketItemGross>();
}
This is happening because you are using integer IDs. When you call database.Load in your transform, you'll need to manually convert it to a string ID.
database.Load<MenuItem>("MenuItems/" + m.ID)
This is one of several places where Raven gets confused if you use integer or guid IDs. If you use string ids, you won't have this problem.
Also, you might consider using a results transformer instead. They are easier than index transformers, which are now obsolete.

RavenDB TransformResults

I'm trying to use the TransformResults feature, and I can't get it to work. I'm not totally sure I understand this feature, perhaps there is another way to solve this problem. What I want is just the Id from the Order and the email addesses from the Customer and the Entrepreneur. I am happy for all tips that can take me in the right direction. Here is my code.
Document
public class OrderDocument
public string Id {get; set }
public EntrepreneurInfo EntrepreneurInfo { get; set; }
public CustomerInfo CustomerInfo { get; set; }
public OrderStatus CurrentOrderStatus { get; set; }
}
Info classes
public class EntrepreneurInfo
{
public string EntrepreneurDocumentId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
public class CustomerInfo
{
public string CustomerDocumentId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
The info classes are just subsets of a Customer and Entrepreneur documents respectively.
The Customer and Entrepreneur documents inherits from a base class ( AbstractOrganizationDocument) that has the EmailAddress property.
My Index
public class OrdersApprovedBroadcastingData :
AbstractIndexCreationTask<OrderDocument, OrdersApprovedBroadcastingData.ReduceResult>
{
public OrdersApprovedBroadcastingData()
{
this.Map = docs => from d in docs
where d.CurrentOrderStatus == OrderStatus.Approved
select new
{
Id = d.Id,
CustomerId = d.CustomerInfo.CustomerDocumentId,
EntrepreneurId = d.EntrepreneurInfo.EntrepreneurDocumentId
};
this.TransformResults = (db, orders) => from o in orders
let customer = db.Load<CustomerDocument>(o.CustomerId)
let entrepreneur = db.Load<EntrepreneurDocument>(o.EntrepreneurId)
select
new
{
o.Id,
o.CustomerId,
CustomerEmail = customer.EmailAddress,
o.EntrepreneurId,
EntrepreneurEmail = entrepreneur.EmailAddress
};
}
public class ReduceResult
{
public string Id { get; set; }
public string CustomerId { get; set; }
public string CustomerEmail { get; set; }
public string EntrepreneurId { get; set; }
public string EntrepreneurEmail { get; set; }
}
}
If I look at the result of this Index in Raven Studio I get null values for all fields except the Id. And finally here is my query.
Query
var items =
this.documentSession.Query<OrdersApprovedBroadcastingData.ReduceResult, OrdersApprovedBroadcastingData>()
.Select(x => new OrdersToBroadcastListItem
{
Id = x.Id,
CustomerEmailAddress = x.CustomerEmail,
EntrepreneurEmailAddress = x.EntrepreneurEmail
}).ToList();
Change your index to:
public class OrdersApprovedBroadcastingData : AbstractIndexCreationTask<OrderDocument>
{
public OrdersApprovedBroadcastingData()
{
Map = docs => from d in docs
where d.CurrentOrderStatus == OrderStatus.Approved
select new
{
};
TransformResults = (db, orders) =>
from o in orders
let customer = db.Load<CustomerDocument>(o.CustomerInfo.CustomerDocumentId)
let entrepreneur = db.Load<EntrepreneurDocument>(o.EntrepreneurInfo.EntrepreneurDocumentId)
select new
{
o.Id,
CustomerEmailAddress = customer.EmailAddress,
EntrepreneurEmailAddress = entrepreneur.EmailAddress
};
}
}
Your result class can simply be the final form of the projection, you don't need the intermediate step:
public class Result
{
public string Id { get; set; }
public string CustomerEmailAddress { get; set; }
public string EntrepreneurEmailAddress { get; set; }
}
You don't have to nest this class in the index if you don't want to. It doesn't matter either way. You can query either with:
var items = session.Query<Result, OrdersApprovedBroadcastingData>();
Or with
var items = session.Query<OrderDocument, OrdersApprovedBroadcastingData>().As<Result>();
Though, with the first way, the convention tends to be to nest the result class, so really it would be
var items = session.Query<OrderDocument.Result, OrdersApprovedBroadcastingData>();
Note in the index map, I am not including any properties at all. None are required for what you asked. However, if you want to add a Where or OrderBy clause to your query, any fields you might want to filter or sort on should be put in there.
One last thing - the convention you're using of OrderDocument, CustomerDocument, EntrepreneurDocument, is a bit strange. The usual convention is just Order, Customer, Entrepreneur. Think of your documents as the persisted form of the entities themselves. The convention you are using will work, it's just not the one usually used.

How to add where clause for child items in Castle ActiveRecord

I am using Castle ActiveRecord and created two entities as follow :
[ActiveRecord("Teams")]
public class Team : ActiveRecordLinqBase<Team>
{
public Team()
{
Members = new List<Member>();
}
[PrimaryKey]
public int Id { get; set; }
[Property("TeamName")]
public string Name { get; set; }
[Property]
public string Description { get; set; }
[HasMany(Inverse = true,
Lazy = true,
Cascade = ManyRelationCascadeEnum.AllDeleteOrphan)]
public virtual IList<Member> Members { get; set; }
}
[ActiveRecord("Members")]
public class Member : ActiveRecordLinqBase<Member>
{
[PrimaryKey]
public int Id { get; set; }
[Property]
public string FirstName { get; set; }
[Property]
public string Lastname { get; set; }
[Property]
public string Address { get; set; }
[BelongsTo("TeamId")]
public Team Team { get; set; }
}
And I used ICriterion to have filtered Team data
IList<ICriterion> where = new List<ICriterion>();
where.Add(Expression.Eq("Name", "name1"));
ICriterion[] criteria = where.ToArray();
var teams = Team.FindAll(criteria);
So far it works well, but I want to add another filter on Members table. The result query would be like this
select *
from Teams t join Member m on t.Id = m.TeamId
where t.Name = 'name1'
and m.Address = 'address'
How to get this done using ICriterion?
I mean how to add criterion for Team.Members property.
Not using LINQ. (I know this could be done using linq easily)
For join you can use
DetachedCriteria
DetachedCriteria criteriaTeam = DetachedCriteria.For<Team>();
DetachedCriteria criteriaMember = criteriaTeam .CreateCriteria("Members");
criteriaTeam .Add(Expression.Eq("Name", "name1"));
criteriaMember.Add(Expression.Eq("Address", "address"));
ICriteria executableCriteria = criteriaTeam .GetExecutableCriteria(session);
executableCriteria.List<Team>();
This will return only Team.To return both Team and Members in a single fetch you can use NHibernate result transformer Projections in NHibernate