Problems with ActiveRecord and LINQ queries - nhibernate

I have a simple class:
public class User : ActiveRecordLinqBase<User>
{
[PrimaryKey(Column = "user_id", Length = 20)]
public string Id { get; set; }
[Property(Column = "password", Length = 16)]
public string Password { get; set; }
...
}
and I have created the following repository:
public class SqlRepository<T> : IRepository<T> where T : ActiveRecordLinqBase<T>, new() {
public void Add(T entity) {
entity.SaveAndFlush();
}
public void Remove(T entity) {
entity.DeleteAndFlush();
}
public void Modify(T entity) {
entity.UpdateAndFlush(); ;
}
...
public IEnumerable<T> FindAll(Func<T, bool> predicate) {
return ActiveRecordLinqBase<T>.Queryable.Where(predicate);
}
}
Now, when running the following unit test (against a MySQL database):
[Test]
public void Test_Sample() {
var repo = new SqlRepository<T>();
repo.Add("john.doe", "keyword1");
repo.Add("other.user", "keyword2");
var users = repo.FindAll(x => x.Username.Contains("john")).ToList();
Assert.AreEqual(1, users.Count);
}
... I get the following SQL query:
SELECT this_.user_id as user1_0_0_, this_.password as password0_0_, this_.role as role0_0_ FROM users this_
Where is the WHERE clause?
If I instead do the following in the same test directly...
var users = User.Queryable.Where(x => x.Username.Contains("john"));
I get the following SQL:
SELECT this_.user_id as user1_0_0_, this_.password as password0_0_, this_.role as role0_0_ FROM users this_ WHERE this_.user_id like ?p0;?p0 = '%john%'
Am I doing something wrong?
What is the difference between those two queries?
Edit: I also tried with
return ActiveRecordLinq.AsQueryable<T>().Where(predicate);
without success.

Now this is just because I like code, and sometimes I notice stuff... I'm no expert on Active Record, so this is just a guess...
Maybe you should change the signature of the FindAll method from
public IEnumerable<T> FindAll(Func<T, bool> predicate)
into
public IEnumerable<T> FindAll(Expression<Func<T, bool>> predicate)
which will allow you to hit the right overload of Where, which is most likely the overload you're looking for.
It's because a Func can't be reflected upon the same way an Expression of Func can.

Related

How can I use SumAsync to calculate sum of a customized value object in Asp.Net core

I have a Model with a property of a value object type as following:
public class Course : AggregateRoot, ISpModel
{
...
public UnsignedNumber MaximumCapacity { get; private set; }
...
}
with UnsignedNumber being a value object containing a short value:
public class UnsignedNumber : BaseValueObject<UnsignedNumber>
{
public short Value { get; }
...
}
What I need to do is to sum all the MaximumCapacities of courses which correspond with certain conditions, but when I try to add a SumAsync(x => x.MaximumCapacity) at the end of the query, I get a syntax error
the syntax error
and when I try to do the same with it's value, I get a linq error in runtime.
"The LINQ expression '(int)(EntityShaperExpression: \r\n EntityType: Course\r\n ValueBufferExpression: \r\n (ProjectionBindingExpression: Outer)\r\n IsNullable: False\r\n).MaximumCapacity.Value' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information."
Edit:
Here's the Linq Expression that encounters the problem:
var query = _dbContext.Courses.AsQueryable();
query = query.Include(x => BunchOfIncludes(x));
var res = await query.Where(x => BunchOfClauses(x)).SumAsync(x => x.MaximumCapacity.Value);
Edit2: the classes mentioned above:
public abstract class AggregateRoot : Entity
{
private readonly List<IDomainEvent> _events;
protected AggregateRoot() => _events = new List<IDomainEvent>();
public AggregateRoot(IEnumerable<IDomainEvent> events)
{
if (events == null) return;
foreach (var #event in events)
((dynamic)this).On((dynamic)#event);
}
protected void AddEvent(IDomainEvent #event) => _events.Add(#event);
public IEnumerable<IDomainEvent> GetEvents() => _events.AsEnumerable();
public void ClearEvents() => _events.Clear();
}
public interface ISpModel
{
}
public abstract class BaseValueObject<TValueObject> : IEquatable<TValueObject>
where TValueObject : BaseValueObject<TValueObject>
{
...
public static bool operator ==(BaseValueObject<TValueObject> right, BaseValueObject<TValueObject> left)
{
if (right is null && left is null)
return true;
if (right is null || left is null)
return false;
return right.Equals(left);
}
...
}

A way to always filter query results with Entity Framework Core

I am using an OrgID on all my models in an ASP.NET application using EF as a way to partition the database. I do this so that the database can be shared among multiple users while ensuring that only the data of their organization is available to them.
This forces me to write this OrgID with every insert and to filter every call I make to the database.
So for instance I query the current user's OrgID and insert this in my controllers' Update methods like this:
store.OrgID = await _userManager.GetUserAsync(User).OrgID;
_context.Update(store);
await _context.SaveChangesAsync();
Then when I want to list out objects I again need to filter by OrgID:
var orgID = await _userManager.GetUserAsync(User).OrgID;
var stores = await _context.Stores.Where(s => s.OrgID == orgID).ToListAsync();
I'd love to find a way to override the ApplicationDBContext so that these are handled automatically otherwise it's quite a tedious and error prone task to always handle this in every call to the database.
Any suggestions would be greatly appreciated.
Check Global Query Filters.
Global query filters are LINQ query predicates (a boolean expression
typically passed to the LINQ Where query operator) applied to Entity
Types in the metadata model (usually in OnModelCreating). Such filters
are automatically applied to any LINQ queries involving those Entity
Types, including Entity Types referenced indirectly, such as through
the use of Include or direct navigation property references.
Create an interface that has the OrgID :
public interface IOrgID
{
public int OrgID { get; set; }
}
All your models must implement this interface e.g.:
public class ApplicationUser : IdentityUser, IOrgID
{
public int OrgID { get; set; }
//...
}
public class Stores : IOrgID
{
public int OrgID { get; set; }
//...
}
Use generic repository and create CRUD methods considering the OrgID from the currently logged in user:
public class MyRepo
{
private readonly ApplicationDbContext _context;
private readonly IHttpContextAccessor _accessor;
private readonly int _orgID;
public MyRepo(ApplicationDbContext context, IHttpContextAccessor accessor)
{
_context = context;
_accessor = accessor;
var userId = _accessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
_orgID = _context.Users.Find(userId).OrgID;
}
public async Task<T> GetAsync<T>(Expression<Func<T, bool>> whereExp)
where T : class, IOrgId
{
return await _context.Set<T>().Where(x => x.OrgId == _orgID).FirstOrDefaultAsync(whereExp);
}
public async Task<bool> Create<T>(T entitiy)
where T : class, IOrgId
{
_context.Set<T>().Add(entitiy);
return await _context.SaveChangesAsync() > 0;
}
public async Task<bool> UpdateAsync<T>(T entity)
where T : class, IOrgId
{
_context.Entry<T>(entity).State = EntityState.Modified;
return await _context.SaveChangesAsync() > 0;
}
public async Task<IEnumerable<T>> ListAsync<T>(Expression<Func<T, bool>> whereExp)
where T : class, IOrgId
{
return await _context.Set<T>().AsNoTracking().Where(x => x.OrgId == _orgID).Where(whereExp).ToListAsync();
}
public async Task<bool> DeleteAync<T>(T entity)
where T : class, IOrgId
{
_context.Entry<T>(entity).State = EntityState.Deleted;
return await _context.SaveChangesAsync() > 0;
}
}
What people do I this case is create a class wrapping the DbContext and exposing methods that make sense for their business logic. In your case you can make a UserRepository/StoreRepository classes where the search methods require a origID paramamter
public class StoreRepository {
private ApplicationDBContext _context
StoreRepository(ApplicationDBContext context){
_context = context
}
public Task<Ilist<Store>> GetStores(int origID){
return _context.Stores.Where(s => s.OrgID == orgID).ToListAsync();
}
}

Jackson - Deserialize with JsonView

I am trying to restrict which properties from a JSON object are deserialised using Jackson JSONViews. The aim is to use this to prevent consumers of my API from submitting data that they shouldn't.
The problem is, I have either misunderstood JSONViews or I am doing something wrong. See below.
I started trying to do this in Spring but have noticed that even the simple test below doesn't work.
Account Class
public class Account {
#Id
private String id;
private String name;
private List<String> items;
private List<User> users;
#JsonView(AccountViews.Private.class)
public void setId(String id) {
this.id = id;
}
#JsonView(AccountViews.Public.class)
public void setName(String name) {
this.name = name;
}
#JsonView(AccountViews.Public.class)
public void setItems(List<String> items) {
this.items = items;
}
#JsonView(AccountViews.Private.class)
public void setUsers(List<User> users) {
this.users = users;
}
}
Views
public class AccountViews {
public interface Public {}
public interface Private extends Public {}
}
Test
#Test
public void testDeserialization(){
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
Account account = mapper.readerWithView(AccountViews.Public.class).forType(Account.class).readValue("{ \"name\": \"account1\", \"items\": [\"item1\"], \"users\": [ { \"firstname\": \"user1_firstname\", \"lastname\": \"user1_lastname\" }] }");
assertEquals(account.getName(), "account1");
assertNull(account.getUsers());
}
Unforunately, the 2nd assertion fails because Users has a user object inside.
Basically, even though "users" is a property of Account, I don't want the value to be deserialized because I have used the JSONView (AccountViews.Public.class). However, whatever I try it always seems to be deserialized and is present on the account object.
Any help much appreciated.
Error
`java.lang.AssertionError: expected null, but was:<[User#609db43b]>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotNull(Assert.java:755)
at org.junit.Assert.assertNull(Assert.java:737)
at org.junit.Assert.assertNull(Assert.java:747)
at`

AutoMapper Update Actions in ASP.NET MVC

This is probably quite straight forward for some, however I'm a bit confused and can't find a decent example. Say I'm using view models and my POST action takes in that view model. Typically I would do something along the following lines:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = Mapper.Map<UserViewModel, User>(uvm);
_repository.Update(user);
return RedirectToAction("Index");
}
Although this isn't the full picture. The mapping would work fine, however if I were to just update what I've mapped then it'd get rid of valuable data in the database because of course in this case I'm not updating the password or other details.
My repository looks something like this:
public void Update(User user)
{
User u = Session.QueryOver<User>().Where(x => x.UserName == user.UserName).SingleOrDefault();
if (u == null)
throw new Exception("User not found");
u.Forename = user.Forename;
u.Surname = user.Surname;
u.EmailAddress = user.EmailAddress;
}
[I'm using NHibernate so it'll save the object back to the DB once the session is closed (after the request has finished) automatically for me.]
So my question is, in my repository should I load the "User" entity, then update the values I want, and then save it back, or is there another method to do this? The reason I ask is because it seems a bit... "manual" if you see what I mean? Perhaps it is correct, but I just wanted to see opinions of those with more experience in this area.
Cheers
I use the following approach:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = _userRepository.FindById(uvm.Id);
user.Forename = uvm.Forename;
user.Surname = uvm.Surname;
user.EmailAddress = uvm.EmailAddress;
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE:
To address the comments about AutoMapper here's how to proceed:
Let's take for example the following classes:
public class UserViewModel
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
}
public class User
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
We don't want to modify the user password in the UI. So we express our intention to AutoMapper:
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
and then:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
// Fetch the original model we would like to update
User user = _userRepository.FindById(uvm.Id);
Mapper.Map(uvm, user);
// At this stage the user model will have its
// Forename, Surname and EmailAddress properties
// updated from the view model and its Password property
// will remain the one we got from the repository
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE 2:
To address the question in the comments about configuring AutoMapper I usually use Profiles:
public class UsersProfile : Profile
{
protected override void Configure()
{
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
Mapper
.CreateMap<User, UserViewModel>();
}
}
and then have a registry class which registers all the mappers:
public class MappingsRegistry
{
public static void Configure()
{
Mapper.AddProfile(new UsersProfile());
Mapper.AddProfile(new SomeOtherProfile());
...
}
}
which is called in Application_Start:
MappingsRegistry.Configure();
Finally my controllers have a reference to the mapping engine:
public class UsersController : Controller
{
private readonly IUsersRepository _repository;
private readonly IMappingEngine _mappingEngine;
public ContratsFCController(IUsersRepository repository, IMappingEngine mapperEngine)
{
_repository = repository;
_mapperEngine = mapperEngine;
}
[AutoMap(typeof(User), typeof(UserViewModel))]
public ActionResult Update(int id)
{
var user = _repository.FindById(id);
return View(user);
}
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
if (!ModelState.IsValid)
{
return View(uvm);
}
var user = _repository.FindById(uvm.Id);
_mapperEngine.Map(uvm, user);
_repository.Update(user);
return RedirectToAction("Index");
}
}
Now all that's left is to instruct your DI framework to pass the Mapper.Engine property to the constructor and in your unit tests obviously substitute them with an appropriate mock.

Fluent nHibernate Automapping not creating Plural table name

I have two tables, Locations and Facilities
They map to two classes,
public Location : Entity
{
//properties
}
public Facility : Entity
{
public virtual Location Location { get; set; }
}
Everything works just dandy, until I change facility to this
public Facility : Location
{
}
Now I get an exception from nHibernate saying
NHibernate.ADOException was unhandled by user code
Message=could not execute query
InnerException: System.Data.SqlClient.SqlException
Message=Invalid object name 'Facility'.
For some reason it is not creating the plural name of the table into the sql string.
Thanks for any help!
EDIT
This is my current TableNameConvention
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
When Facility inherits from Entity, the Facility does run through this method. When it inherits from Location, it does not
Edit 2
Figured I'd post everything...
public class AutoPersistenceModelGenerator : IAutoPersistenceModelGenerator
{
#region IAutoPersistenceModelGenerator Members
public AutoPersistenceModel Generate()
{
var mappings = new AutoPersistenceModel();
mappings.AddEntityAssembly(typeof(Person).Assembly).Where(GetAutoMappingFilter);
mappings.Conventions.Setup(GetConventions());
mappings.Setup(GetSetup());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
return mappings;
}
#endregion
private Action<AutoMappingExpressions> GetSetup()
{
return c =>
{
c.FindIdentity = type => type.Name == "Id";
};
}
private Action<IConventionFinder> GetConventions()
{
return c =>
{
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ForeignKeyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.HasManyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.HasManyToManyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ManyToManyTableNameConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.PrimaryKeyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ReferenceConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.TableNameConvention>();
};
}
/// <summary>
/// Provides a filter for only including types which inherit from the IEntityWithTypedId interface.
/// </summary>
private bool GetAutoMappingFilter(Type t)
{
return t.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEntityWithTypedId<>));
}
}
Have you set a convention?
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
string typeName = instance.EntityType.Name;
instance.Table(Inflector.Net.Inflector.Pluralize(typeName));
}
}
This is an old question, but for the sake of others who stumble upon this looking for an answer, you can also create a convention that uses the built-in PluralizationService that comes with EF:
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
string typeName = instance.EntityType.Name;
instance.Table(PluralizationService.CreateService(CultureInfo.CurrentCulture).Pluralize(typeName));
}
}