Workaround for selectmany in ravendb using client api - ravendb

I have a ravendb class like such:
public class Student
{
public string Id { get; set; }
public string TopLevelProperty { get; set; }
public Dictionary&ltstring, string&gt Attributes { get; set; }
public Dictionary&ltstring,List&ltDictionary&ltstring, string&gt&gt&gt CategoryAttributes { get; set; }
}
and a document like so:
The following linq will not work due to a selectmany:
test = (from student in session.Query()
from eduhistory in student.CategoryAttributes["EducationHistory"]
where eduhistory["StartYear"] == "2009"
select student).ToList();
How can I get all students where StartYear == 2009?

This does it :
test = session.Advanced.LuceneQuery()
.Where("CategoryAttributes.EducationHistory,StartYear:2009")
.ToList();
Notice the comma rather than a dot after EducationHistory. This indicates that we are looking at the list to find a property in one of the items named StartYear.
If I wanted greater than :
test = session.Advanced.LuceneQuery()
.Where("CategoryAttributes.EducationHistory,StartYear:[2009 TO null]")
.ToList();
etc etc.

This should work:
test = (from student in session.Query()
where student.CategoryAttributes["EducationHistory"].Any(edu => edu["StartYear"]== "2009" )
select student).ToList();

Related

Returning a String from a SQL query in MVC Entity Framework 6 Controller

I want to execute a basic SQL query (e.g. "SELECT string FROM table WHERE criteria = TRUE) on MVC Entity Framework 6. The returned type is to be a string. I have tried many possible things but they return the SQL query string or something completely irrelevant, such as:
SELECT [Extent1].[Fname] AS [Fname]
FROM [dbo].[Developer] AS [Extent1]
WHERE ([Extent1].[Login] = #p__linq__0)
OR (([Extent1].[Login] IS NULL) AND (#p__linq__0 IS NULL))
I have the following in my controller:
var data = db.Developers.Where(y => y.Login.Equals(username)).ToList();
This is my Developers class:
public partial class Developer
{
public int PersonID { get; set; }
public string Fname
{
get { return Fname.ToString(); }
set { Fname = value;}
}
public string Lname { get; set; }
public string Developer1 { get; set; }
public string Login { get; set; }
public Nullable<bool> IsManager { get; set; }
public Nullable<bool> IsPF { get; set; }
public Nullable<bool> IsLW { get; set; }
public string Prov { get; set; }
}
I would like to get the string that this query returns.
Somehow this works when this is made to a List and passed to the View model, where it is displayed using #Html.DisplayFor().
Is there a way to execute my SQL command in my controller and get a string value in return?
FirstOrDefault() to return the first element, in this case there is only one.
You should include a validation if username doesnt exist.
Developer myDeveloper = db.Developers
.Where(y => y.Login.Equals(username))
.ToList()
.FirstOrDefault();
return myDeveloper.Lname;

Linq - How to to create a list of categories which are included in another table

I am trying to select from a list of categories where it matches the category type of a list of items using linq. IE, from a list of all the FIGstationeryCategories, select only the ones where the FiGStationeryType has a matching category from an already filtered list. The models are listed below.
public class FIGstationeryType
{
public int Id { get; set; }
public virtual FIGstationeryCategory Category { get; set; }
public virtual FIGcompany Company { get; set; }
public decimal Height { get; set; }
public decimal Width { get; set; }
public virtual FIGstationeryType Template { get; set; }
public bool DoubleSided { get; set; }
}
public class FIGstationeryCategory
{
public int Id { get; set; }
public string Name { get; set; }
public decimal MaxZoom { get; set; }
public ICollection<FIGstationeryType> StationeryItems { get; set; }
}
I have been going around in circles with this, and any help will be much appreciated. I haven't got very far! The first line of code works fine, it is the second one I am struggling with.
var listOfItems = db.StationeryTypes
.Where(C => C.Company.Users.Any(u => u.UserId == WebSecurity.CurrentUserId))
.ToList();
var categoryList = db.StationeryCategories
.Where(listOfItems
Any help would be much appreciated.
var listOfCategories =
(from o in listOfItems select o.Category.Name).Distinct().ToList();
When I thought about it (After watching 3 hours of linq videos last night), I realised that the listOfItems already held all the categories which where in use, so I didn't need to query and compare the two tables, just draw the relevant values from the list I already had.
I am not entirely sure how you want to select your categories but this probably goes a little way:
var categoryList = db.StationeryCategories
.*Select*(x => listOfItems.Where(y => y.Category == x)
.FirstOrDefault());
Can you clarify if this is the criteria you are after?

RavenDb Select() downcasts instead of selecting the neccessary fields

public class PersonBrief
{
public int Id { get; set; }
public string Picture { get; set; }
public PersonBrief(Person person)
{
Id = person.Id;
Picture = person.Picture;
}
}
public class Person : PersonBrief
{
public string FullName { get; set; }
}
var results = session.Query<Person>()
.Select(x => new PersonBrief(x))
.ToList();
Assert.IsNull(results[0] as Person); // Fails
Is this a bug? If not, what would be the correct way to select only the fields i'm interested in?
It would work if you move the .ToList before the .Select, but that would be doing the work on the client.
If you want to do it on the server, you need to use As in your query, and you need a static index that does a TransformResults. See these docs.

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