RavenDB - where condition on included object - ravendb

Lets have theese objects:
public class Obj1
{
public string Id { get; set; }
}
public class Obj2
{
public string Id { get; set; }
public string Obj1Id { get; set; }
}
public class Obj3
{
public string Id { get; set; }
public string Obj2Id { get; set; }
}
I use include like this:
var objs3 = session.Query<Obj3>()
.Customize(x => x.Include<Obj3>(o3 => o3.Obj2Id))
.Take(1000)
.ToList();
foreach (var obj3 in objs3)
{
var obj2 = session.Load<Obj2>(obj3.Obj2Id);
//do something with it
}
My question is, is there possibility to add to query something like .Where(o2 => o2.Obj1Id == "some/Id")? Query knows nothing about Obj2 on the client (Linq) side, but server side works with them, because this makes only one request to the DB.
I try to look how to works indexes and projections, but no luck with some constructions. Maybe my view is deformed from relational databases and there are other solutions, that need my structures to be redefined...
My other solution is to add Obj1Id to Obj3, but that will makes duplicites (I can live with this ;) )
Some extra info to objects:
- Obj1 is unique for each customer, so there is about 200 documents
- Obj2 : Obj3 is 1 : 1 siblings, each with own point of view properties, both about 100.000 for each customer
Bonus question: Is there something like Inclide chaining? Including Obj1 inside of Including Obj2? (this I won't use, is just question)

No, you can't add condition to the include.
It is either all the way in, or not at all.

As Oren said, you have condition within the include. So you can either restructure your data, or use a multi map (and maybe also reduce) index if both Obj2 and Obj3 have something in common, like the Id of Obj1.
Which way is better in your case really depends on the kind of data you have, no general answer here. (Maybe you can post another, more concrete question with actual class-names instead of abstract names like Obj1-3).

Related

Does including Collections in Entities violate what an entity is supposed to be?

I am building a Web API using Dapper for .NET Core and trying to adhere to Clean Architecture principles. The API is consumed by an external Angular front-end.
I have repositories that use Dapper to retrieve data from the database, and this data then passes through a service to be mapped into a DTO for display to the user.
It is my understanding that an entity should be an exact representation of the database object, with no extra properties, and that I should use DTOs if I require some additional properties to show the user (or if I wish to obscure certain properties from the user too).
Suppose I have a DTO:
public class StudentDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Assignment> Assignments { get; set;}
}
and its corresponding Entity:
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
}
With this model, should I want to get a student with all of their assignments, I'd need to have two repository calls, and do something like this in the service:
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
var assignments = this.assignmentRepository.GetByStudentId(id);
return SomeMapperClass.Map(student, assignments);
}
But this seems inefficient and unnecessary. My question is, should I not just retrieve the Assignments when I get the student entity in the repository, using a JOIN? Or would this violate what an entity is supposed to be?
I apologise, I do realise this is a rather simple question, but I'd really like to know which method is the best approach, or if they both have their use cases
I think it would be more efficient, since map uses reflections, that is slower tens times
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
student.Assignments = this.assignmentRepository.GetByStudentId(id);
return student;
}
but the common way is
return _context.Students.Include(i=>i.Assignments).FirstOrDefault(i=> i.Id==id);
This is why the generic repository is a bad idea in the most casses, since it is hard to guess what set of data you will need.

nHibernate- a Collection which would contain only a supertype

I have the following classes:
class Employee
{
public string Name { get; set; }
}
class HistoricalEmployee : Employee
{
public DateTime TerminationDate { get; set; }
}
class Company
{
public IList<Person> CurrentEmployees { get; set; }
}
Employee and HistoricalEmployee are mapped using table-per-class-heirarchy strategy.
When I retrieve the CurrentEmployees collection, I want it only to contain elements that are Employee, and NOT HistoricalEmployees.
when an employee 'dies', they're not really deleted, but they become HistoricalEmployee (with a few more attributes, such as termination date etc.).
Obviously, over time, the number of HistoricalEmployees will exceed the number of Employees by magnitudes, so I can't fetch all HistoricalEmployees when I only need current Employees.
How can I (fluently) configure the collection to only retrieve elements of the super class?
I think it's something to do with the Polymorphism property, but I couldn't really figure out how to do that.
thanks,
Jhonny
Ok, I did this like so:
mapping.HasMany(x => x.CurrentEmployees)
//.Where(pqa => pqa.TimeOut != null)
.Where("TerminationDate is null")
apparently, the .Where() function creates a filter on the property, which is exactly what I needed.
notice that I used the string version, and commented-out the Func<> version.
This is because that currently (FNH 1.1), as far as I could determine, the Func<> version doesn't work.
hopes this helps somebody,
J

WCF REST : returning classes and rules / Pascal/Camel case for fields?

Can anyone tell me the recomemended case (pascal or camel) for returning classes with fields... For example, the example that comes with vs 2010 uses Pascal Case like so
// TODO: Edit the SampleItem class
public class SampleItem
{
public int Id { get; set; }
public string StringValue { get; set; }
}
Notice first capital letter on Id and StringValue. I was wondering is this the recommended way? A lot of public services seem to return camelCase as fields.
I must admit it feels more natural with Pascal Case which follows the microsoft naming conventions for Properties etc.
Also the properties are going to be singular because its for 1 record i.e. Id, StringValue etc.. but what about the class name, i presume this will be singular name also as the XML that i return will make an array of SampleItem ??
I am just sort of looking for a bit of confirmation really.
The class i return will contain fields for my specific returned data, is there any fields i should be including by default.... I think not?? As if it fails i just return Error 400 so i don't need to supply any Error Number, Error Desc etc in each class
Any comments really appreciated
EDIT
here is an exmaple of the method i am using to return the xml ... its the default method in the standard vs 2010 template
[WebGet(UriTemplate = "")]
public List<SampleItem> GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
}
Of course this method returns a LIST (only an exmaple) of SampleItem..
SampleItem is a class and here it is
// TODO: Edit the SampleItem class
public class SampleItem
{
public int Id { get; set; }
public string StringValue { get; set; }
}
The Microsoft Naming Conventions suggest PascalCasing for properties, and camelCasing for parameters. Therefore, your current casing is correct, if your goal is to match Microsoft's standards.

'Static/Constant' business objects

I don't quite know how to ask this question, so I'll phrase it as an example instead:
Imagine in an application you have a Country object. There are two properties of this object: Name, and a 'Bordering Countries' collection. More properties might be added later, but it will be the kind of information that would change very rarely (e.g. changes of country names/borders)
Lets say this application needs to know about all of the countries in the world. Where would you store these object's state? How would you new them up? It seems silly to store all this state in the DB, since it won't change very often.
One option might be to have an abstract 'country' base object, and have a class for each country inheriting from this with the details of each country. But this doesn't seem quite right to me.
What is the proper way of dealing with these kinds of objects?
UPDATES:
Someone asked about language: C#
Also, I'm coming at this from a web application perspective, so there wouldn't be multiple client installations where I'd have to worry about updating hard coded values.
Most people have suggested not hardcoding the data, but using the DB or XML files to store the data. Could anyone provide an example of how this kind of object would be 'newed up' (from e.g. an XML file)? Would you use some kind of helper or factory method to obtain instance of a particular country?
Definatly in the DB. Load them up once (refresh periodically) and use them from there.
I say definatly DB, as you will most likely be extracting data for reporting purposes, and if structured correctly, you can reuse the country data for other applications too.
enumerations are quite useful for that kind of thing. you don't mention the language you're using, but in java, they're stored quite efficiently as an ordinal index (integer) and you can always add new values to the end of the enumeration list.
I don't think that create a class hierachy would be a good design for your problem. Instead I'd store the values at the database and have a generic Country class what retrieve the country state from database.
It won't change very often, you say, but in that you indicate that it may change at one point. In such cases, you should put it in a datastore of some sorts, as the others have indicated - be it a database, text file, or otherwise.
Keep in mind that, if you put it in an enum or a static class or some other part of your actual application, the end-user will not be able to change it easily. So if by some natural disaster or world war 3 some countries disappear, merge, separate, get a new government type or name, you need to be able to update your country list.
You could just have the application read the country list on startup, and leave it in memory. It'll auto-refresh when the application restarts. Another option would be to have it check for updated countries every X period and update the internal list, if it's an application that runs for a long time.
As oedo said, enumumerations should do the job, but if you need more than an index, you could use an xml file. it would be dynamic, no need compilation
You've got a variety of answers, so I thought I'd add my $0.02 worth.
Personally I alway hard code fixed lists like this (same with postcodes). That being said, when I'm at your position, I'll always optimize for readability. ie What will make sense in 6 months time, when you've forgotten about this project and have to do some maintenance?
If I had to do it with a database:
public class Country
{
public string Name { get; set; }
public Country[] BorderingCountries { get; set; }
public Country(iDB db, string name)
{
BorderingCountries = db.BorderingCountriesGet(name);
}
}
Your unit test:
public UnitTest1()
{
iDB db = new DB();
Country c = new Country(db, "Spain");
Assert.AreEqual(2, c.BorderingCountries.Count());
Assert.AreEqual(1, c.BorderingCountries.Count(b => b.Name == "France"));
Assert.AreEqual(1, c.BorderingCountries.Count(b => b.Name == "Portugal"));
}
Oops! You probably want to populate the whole list (not one at a time!)
DB:
static void Main(string[] args)
{
Countries countries = new Countries(new DB());
}
public class Countries
{
public List<Country> Items { get; set; }
public Countries(iDB db)
{
tblCountry[] countries = db.BorderingCountries();
Items = new List<Country>();
Country country = null;
foreach (var c in countries)
{
if (country == null || country.Name != c.Name)
{
country = new Country(c.Name);
Items.Add(country);
}
country.BorderingCountries.Add(new Country(c.BorderingCountry));
}
}
}
public class Country
{
public string Name { get; set; }
public List<Country> BorderingCountries { get; set; }
public Country(string name)
{
this.Name = name;
BorderingCountries = new List<Country>();
}
}
public interface iDB
{
tblCountry[] BorderingCountries();
}
public class DB : iDB
{
public tblCountry[] BorderingCountries()
{
using (DataClassesDataContext dc = new DataClassesDataContext())
{
return dc.tblCountries.ToArray();
}
}
}
If I was hardcoding it:
public class Countries
{
public List<Country> Items { get; set; }
public Countries()
{
Items = new List<Country>();
Items.Add(new Country { Name = "Spain", BorderingCountries = new string[] { "France", "Portugal" }});
Items.Add(new Country { Name = "France", BorderingCountries = new string[] {"Spain","Belgium"});
}
}

NHibernate add unmapped column in interceptor

I'm trying to save a mapped entity using NHibernate but my insert to the database fails because the underlying table has a column that does not allow nulls and IS NOT mapped in my domain object. The reason it isn't mapped is because the column in question supports a legacy application and has no relevance to my application - so I'd like to not pollute my entity with the legacy property.
I know I could use a private field inside my class - but this still feels nasty to me. I've read that I can use an NHibernate interceptor and override the OnSave() method to add in the new column right before my entity is saved. This is proving difficult since I can't work out how to add an instance of Nhibernate.type.IType to the types parameter of my interceptor's OnSave.
My Entity roughly looks like this:
public class Client
{
public virtual int Id { get; set; }
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual string Url { get; set; }
}
And my interceptor
public class ClientInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
if (entity is Client)
{
/*
manually add the COM_HOLD column to the Client entity
*/
List<string> pn_list = propertyNames.ToList();
pn_list.Add("COM_HOLD");
propertyNames = pn_list.ToArray();
List<Object> _state = state.ToList();
_state.Add(false);
state = _state.ToArray();
//somehow add an IType to types param ??
}
return base.OnSave(entity, id, state, propertyNames, types);
}
}
Does anyone have any ideas on how to do this properly?
I can't say for sure since I've never actually done this (like Stefan, I also prefer to just add a private property), but can you just add a NHibernate.Type.BooleanType to the types array?
List<IType> typeList = types.ToList();
typeList.Add(new BooleanType());
types = typesList.ToArray();
EDIT
Yes, it looks like you are right; the types have an internal constructor. I did some digging and found TypeFactory:
Applications should use static
methods and constants on
NHibernate.NHibernateUtil if the
default IType is good enough. For example, the TypeFactory should only
be used when the String needs to have a length of 300 instead of 255. At this point
NHibernate.String does not get you thecorrect IType. Instead use TypeFactory.GetString(300) and keep a
local variable that holds a reference to the IType.
So it looks like what you want is NHibernateUtil:
Provides access to the full range of
NHibernate built-in types. IType
instances may be used to bind values
to query parameters. Also a factory
for new Blobs and Clobs.
typeList.Add(NHibernateUtil.Boolean);
Personally I wouldn't do it so complicated. I would add the private property and assign it a default value - finished. You could also consider a default value in the database, then you don't need to do anything else.
private virtual bool COM_HOLD
{
get { return false; }
set { /* make NH happy */ }
}
Before writing a interceptor for that I would consider to write a database trigger. Because with the Interceptor you are "polluting" your data access layer. It could make it unstable and you could have strange problems.