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"});
}
}
Related
Often in my team people forget to map certain fields from an input object to an output object. I wanted to write a library for unit testing, that checks if all properties on an output object have been filled with a value different than the default value, if not, an exception should be thrown. Ofcourse certain properties will need to be able to be excluded.
I noticed that Fluent Assertions can already do this with the .Should().BeEquivalentTo() Graph comparison.
However, I when a property is not present on the input object, I run into some trouble. Given the following objects:
public class Input
{
public int Age{ get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Output
{
public int Age { get; set; }
public string FullName{ get; set; }
public static Output CreateFrom(Input i)
{
return new Output
{
Age = i.Age,
FullName= $"{i.FirstName} {i.LastName}"
};
}
}
When I do the following assertion:
var input = new Input
{
Age = 33,
FirstName = "Foo",
LastName = "Bar"
};
var output = Output.CreateFrom(input);
var fullName = $"{input.FirstName} {input.LastName}";
input.Should().BeEquivalentTo(output,
o => o.Using<string>(i => i.Subject.Should().Be(fullName)).When(
info => info.Path.Contains(nameof(output.FullName)))
);
I get an exception that FullName is not a property on the Input object, which is fair, but I can't use .Excluding(o => o.FullName) in this case because that would skip assertion all togheter.
I could use .Excluding(o => o.FullName), and write a seperate assertion below it as follows:
output.FullName.Should().Be(fullName);
but that doesn't fix the problem I'm trying to solve, I want every property to be mapped, OR have a specific assertion in BeEquivalentTo so people don't forget to write mappings. And they can still forget to write this seperate assertion when they add .Exluding.
The .WithMapping<Input>() extension method will also not work, since you can assign one property on the input, to another property on the output, but doesn't account for the scenario described above.
IS there a way to do this with Fluent Assertions? That would be my preference since it's already included in the project. Are there anylibraries that tackle this specific scenario, or am I going to have to write this myself?
Thanks!
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.
I am trying to create a static index for the following sample class:
public class Board {
...other assorted fields
List<dynamic> Messages {get; set;}
internal Board() {Messages = new List<dynamic>();}
}
The index is to filter boards which have messages which are a older than a certain date. The aim is to perform an "update" operation on messages which are due today, update their content, and persist them back. The index is needed to avoid traversing all the messages for a board for all clients as that may be computationally expensive. Messages is a list of message types which inherit from a base class which contains a property ExpiryDate.
Trying to create an index like follows results in an "An expression tree may not contain a
dynamic operation" error. I know that the dynamic type does not play well with Linq queries hence the need to use LuceneQueries instead of Query() in RavenDB. Is there any way to make this index work with dynamic properties? Thanks!
public class ScanBoardMessagesIndex : AbstractIndexCreationTask<Board>
{
public ScanBoardMessagesIndex () {
Map = boards => from board in boards
where board.Messages.Any(msg => ((MessageItem) msg).ExpiryDate <= DateTime.UtcNow.Date)
select board;
}
}
EDIT:
I ran into a raven serialization issue because the metadata clr-type of existing Board documents was set to a class namespace which was not valid anymore. I am doing a migration project so I went ahead and first issued a patch to change the metadata clr-type of the existing documents before migrating them to the new data structure which uses a base/abstract class for list of Messages instead of type dynamic.
A Map/Reduce index seems more appropriate for the given requirements. Effectively, you want to be able to query boards by the oldest expiry date of messages in the board. This is an aggregating operation, exactly what Map/Reduce was designed to solve. Also, using a base class for messages will allow you to define the index without resorting to the lower level IndexDefinition:
public class Message
{
public DateTime ExpiryDate { get; set; }
}
public class Board
{
public string Id { get; set; }
public List<Message> Messages { get; set; }
}
public class OldestExpiryDateMessageInBoard : AbstractIndexCreationTask<Board, OldestExpiryDateMessageInBoard.Result>
{
class Result
{
public string BoardId { get; set; }
public DateTime OldestExpiryDate { get; set; }
}
public OldestExpiryDateMessageInBoard()
{
this.Map = boards => from board in boards
from message in board.Messages
select new
{
BoardId = board.Id,
OldestExpiryDate = message.ExpiryDate
};
this.Reduce = results => from result in results
group result by result.BoardId into g
select new
{
BoardId = g.Key,
OldestExpiryDate = g.Min(x => x.OldestExpiryDate)
};
}
}
You can then query this index with Lucene syntax.
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
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.