Is in .NET Core something similar to ActiveRecord in Ruby on Rails?

Viewed 927

I am switching from Rails to .NET Core, but what I really miss is ActiveRecord ORM. In models you just define relationship:

#Town Model
class Town < ApplicationRecord
  belongs_to :country
end

#Country Model
class Country < ApplicationRecord
  has_many :towns
end

Then you can simple obtain list of all Towns in specific Country:

@country = Country.find(params[:id])
@towns = @country.towns

That's very clear, especially in situations, where u are chaining multiple models over ids. In .Net Core i usually end up on something like this:

Task<List<Town>> towns = await _context.Towns.Where(x => x.CountryId == countryId).ToListAsync();

That's still acceptable, but only because there is only model - model relationship.

Lets say we want to obtain Continent of selected Town in Rails:

@town = Town.find(params[:id])
@continent = @town.country.continent

In .NET Core, now I have to work with Joins and final result will be quite complex, its easy to do mistake here (and its not even complex situation). Image having very complex SQL query, LINQ doesnt help u much and u are very close to writing pure SQL.

That's why I ask, if is in .NET Core something similar to very handy ActiveRecord ORM in Rails.

Thanks for your reply and time.

1 Answers

EF supports navigation properties out of the box, so in simple situations you don't need joins. Also you can use lazy loading if you want to. See more information here, here and here for example.

In your case if you have correctly setup relations between entities you can translate

@country = Country.find(params[:id])
@towns = @country.towns

Into:

var country = await _context.Country.Include(c => c.Towns).FindAsync(countryId); 
// or await _context.Country.Include(c => c.Towns).FirstAsync(c => c.Id == countryId) 
var towns = country.Towns;

Or with lazy loading enabled:

var country = await _context.Country.FindAsync(countryId); 
var towns = country.Towns;

The second snippet:

@town = Town.find(params[:id])
@continent = @town.country.continent

Can be translated to:

var town = await _context.Town
     .Include(t => t.Country)
     .ThenInclude(c => c.Continent)
     .FindAsync(TownId); 
var country = town.Country.Continent;
Related