Enum flags with nhibernate / fluent - c#

I am using nhibernate 3 and fluent nhibernate
I have this enum
[Flags]
public enum Permission
{
View = 1,
Add = 2,
Edit = 4,
Delete = 8,
All = View | Add | Edit | Delete
}
Now say I want to find all users that have "view" or "all".
How could I do this with nhibernate(linq)?
session.Query<TableA>().Where x.Permission == Permission.View); // does not bring back all
session.Query<TableA>().Where x.Permission.HasFlag(Permission.View); // crashes
Is there away to do this with having to go
session.Query<TableA>().Where x.Permission == Permission.View || x.Permission == Permission.All);
Edit
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Permission).Not.Nullable().CustomType<PermissionTypes>();
}
}

If you have the following mapping for your TableA type:
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
...
Map(x => x.Permission).CustomType(typeof(Permission)).Not.Nullable();
...
}
}
Then you should be able to issue the following query in your repository or wherever you do your data ccess:
public IList<TableA> GetTableByPermission(Permission permission)
{
return Session.Query<TableA>
.Where(x => x.Permission == permission ||
x.Permission == Permission.All)
.ToList();
}
Or you can check whether a single TableA instance has got the required permission:
public bool HasPermission(Guid guid, Permission permission)
{
var tableA = Session.Query<TableA>.SingleOrDefault(x => x.Id == guid);
if (tableA != null)
return (tableA.Permission & permission) == permission;
// Return false or whatever is appropriate.
return false;
}
You cannot use your HasFlag() method in the query, NHibernate does not understand how to use that in the query.

Related

How to return an object with a collection instead of just the collection from a Web API?

At the moment, in my controller's service method GetSubAccounts(accountId), I have this:
Account account = await context.Accounts.SingleOrDefaultAsync(x => x.Id == accountId);
IQueryable<Account> subAccounts = context.Accounts.Include(x => x.AccountCodes).AsNoTracking();
return await mapper.ProjectTo<SubAccountViewModel>(subAccounts, null, x => x.SubAccounts)
.Where(x => x.PersonId == account.PersonId && x.AccountId != null).ToListAsync();
My SubAccountViewModel is as follows: (note that it has a collection of itself)
public class SubAccountViewModel : Base.AccountViewModel
{
public virtual ICollection<AccountCodeViewModel> AccountCodes { get; set; }
public virtual ICollection<SubAccountViewModel> SubAccounts { get; set; }
}
My mapping profile is:
internal class SubAccountMappingProfile : Profile
{
public SubAccountMappingProfile()
{
CreateMap<Account, SubAccountViewModel>()
.ForMember(x => x.AccountCodes, options => options.ExplicitExpansion())
.ForMember(x => x.SubAccounts, options => options.ExplicitExpansion())
.ReverseMap();
}
}
And this is the JSON I'm getting as a result:
[
{
"id":"c236718f-9d91-4eea-91ee-66760a716343",
"personId":"06d3857d-6a49-4e1c-b63c-7edc83d30cbd",
"accountId":null,
"username":"test same person",
"email":"testsameperson#gmail.com",
"birthDate":"2021-01-02",
"subaccounts":null
}
]
The problem:
I'm getting a top-level array of subaccounts for the accountId parameter I pass to the method. Fine. (There's just one, but nevermind that.)
What I do want is the main account at top-level, with the array of subaccounts as part of it.
I.e.
{
"id":"f61fedc2-eb60-4ba9-9d17-8d41b9cae9f1",
"personId":"06d3857d-6a49-4e1c-b63c-7edc83d30cbd",
"accountId":"f61fedc2-eb60-4ba9-9d17-8d41b9cae9f1",
"username":"test person",
"email":"testperson#gmail.com",
"birthDate":"2021-01-01",
"subaccounts":[
{
"id":"c236718f-9d91-4eea-91ee-66760a716343",
"personId":"06d3857d-6a49-4e1c-b63c-7edc83d30cbd",
"accountId":"f61fedc2-eb60-4ba9-9d17-8d41b9cae9f1",
"username":"test same person",
"email":"testsameperson#gmail.com",
"birthDate":"2021-01-02",
"subaccounts":null
}
]
}
How do I do it?
The problem was one of logic.
To start with, my service method (and my API controller) was returning Task<IEnumerable<SubAccountViewModel>>, when it should return Task<SubAccountViewModel>.
Then my solution was:
Account account = await context.Accounts.SingleOrDefaultAsync(x => x.Id == accountId);
IQueryable<Account> accounts = context.Accounts.AsNoTracking();
SubAccountViewModel subAccountViewModel = await mapper.ProjectTo<SubAccountViewModel>(accounts, null, x => x.AccountCodes)
.SingleOrDefaultAsync(x => x.Id == accountId);
subAccountViewModel.SubAccounts = await mapper.ProjectTo<SubAccountViewModel>(accounts, null, x => x.AccountCodes, x => x.SubAccounts)
.Where(x => x.PersonId == account.PersonId && x.AccountId != null).ToListAsync();
return subAccountViewModel;
This returns the resultset I wanted.

ForAllMembers overriding Ignore rules already defined

I am using AutoMapper and I need to ignore members where an Attribute is not defined. Then, if the Member is not being ignored, I need to map only where values are defined. I have managed to achieve these two separately, but ForAllMembers/ForAllOtherMembers seems to be overriding the first rule.
Let's say I have this class:
public class Foo
{
[MyCustomAttribute]
public string Name { get; set; }
public string IgnoreMe { get; set; }
[MyCustomAttribute]
public int? DontIgnoreNumber { get; set; }
}
I want to ignore IgnoreMe regardless. Then, for Name and DontIgnoreNumber, I want to map them only if they have a value. How can I achieve this?
I have tried this:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Foo, Foo>()
.IgnoreAllNonAttributedEntities()
.ForAllOtherMembers(opts =>
{
opts.Condition((src, dest, srcMember) =>
{
// Check if source is a default value
return srcMember != null;
});
});
});
I have checked that the ForAllOtherMembers rule is working. And I, separately, have checked that the IgnoreAllNonAttributedEntities is working. When I try to combine them, the ForAllOtherMembers seems to be taking priority.
IgnoreAllNonAttributedEntities is defined as:
public static IMappingExpression<TSource, TDestination> IgnoreAllNonAttributedEntities<TSource, TDestination>
(this IMappingExpression<TSource, TDestination> expression)
{
var flags = BindingFlags.Public | BindingFlags.Instance;
//var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
foreach(var prop in destinationType.GetProperties(flags))
{
var attr = ReflectionHelpers.GetAttribute<MyCustomAttribute>(prop);
if (attr == null)
{
expression.ForMember(prop.Name, opt => opt.Ignore());
}
}
return expression;
}
I've just run your code and it works as expected. However, maybe what bothers you is the default value of value types in c# (cuz you only check for nulls). Here is my fix for value types:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Foo, Foo>()
.IgnoreAllNonAttributedEntities()
.ForAllOtherMembers(opts =>
{
opts.Condition((src, dest, srcMember) =>
{
var srcType = srcMember?.GetType();
if (srcType is null)
{
return false;
}
return (srcType.IsClass && srcMember != null)
|| (srcType.IsValueType
&& !srcMember.Equals(Activator.CreateInstance(srcType)));
});
});
});
I've recreated your scenerio using latest version of automapper available on NuGet (8.0.0.0).

Entity Framework 7 audit log

I am porting an old project over to ASP.NET 5 and Entity Framework 7. I have used the database first approach (DNX scaffold) to create the model.
The old project is based on Entity Framework 4 and audit tracking is implemented by overriding the SaveChanges method of the DbContext:
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
int? UserId = null;
if (System.Web.HttpContext.Current != null)
UserId = (from user in Users.Where(u => u.UserName == System.Web.HttpContext.Current.User.Identity.Name) select user.Id).SingleOrDefault();
foreach (ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
{
Type EntityType = entry.Entity.GetType();
PropertyInfo pCreated = EntityType.GetProperty("Created");
PropertyInfo pCreatedById = EntityType.GetProperty("CreatedById");
PropertyInfo pModified = EntityType.GetProperty("Modified");
PropertyInfo pModifiedById = EntityType.GetProperty("ModifiedById");
if (entry.State == EntityState.Added)
{
if (pCreated != null)
pCreated.SetValue(entry.Entity, DateTime.Now, new object[0]);
if (pCreatedById != null && UserId != null)
pCreatedById.SetValue(entry.Entity, UserId, new object[0]);
}
if (pModified != null)
pModified.SetValue(entry.Entity, DateTime.Now, new object[0]);
if (pModifiedById != null && UserId != null)
pModifiedById.SetValue(entry.Entity, UserId, new object[0]);
}
}
return base.SaveChanges(options);
}
My question is, how can I implement this in Entity Framework 7? Do I have to take the code first approach?
Basically you have two ways to achieve this:
Using ChangeTracker API (EF 6+):
This is the way we currently do it in EF 6 and it is still valid and working for EF 7:
First you have to make sure your entities are implementing a common interface for audit fields:
public interface IAuditableEntity
{
int? CreatedById { get; set; }
DateTime Created { get; set; }
int? ModifiedById { get; set; }
DateTime Modified { get; set; }
}
Then you can override SaveChanges and update each common field with audit values:
public override int SaveChanges()
{
int? userId = null;
if (System.Web.HttpContext.Current != null)
userId = (from user in Users.Where(u => u.UserName == System.Web.HttpContext.Current.User.Identity.Name) select user.Id).SingleOrDefault();
var modifiedEntries = ChangeTracker.Entries<IAuditableEntity>()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
foreach (EntityEntry<IAuditableEntity> entry in modifiedEntries)
{
entry.Entity.ModifiedById = UserId;
entry.Entity.Modified = DateTime.Now;
if (entry.State == EntityState.Added)
{
entry.Entity.CreatedById = UserId;
entry.Entity.Created = DateTime.Now;
}
}
return base.SaveChanges();
}
Using EF 7 new "Shadow Properties" Feature:
Shadow properties are properties that do not exist in your entity class. The value and state of these properties is maintained purely in the Change Tracker.
In other words, the audit columns will not be exposed on your entities which seems to be a better option compare to the one above where you have to include them on your entities.
To implement shadow properties, first you have to configure them on your entities. Let's say for example you have a User object that needs to have some audit columns:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Property<int>("CreatedById");
modelBuilder.Entity<User>().Property<DateTime>("Created");
modelBuilder.Entity<User>().Property<int>("ModifiedById");
modelBuilder.Entity<User>().Property<DateTime>("Modified");
}
Once configured, now you can access them on SaveChanges() override and update their values accordingly:
public override int SaveChanges()
{
int? userId = null;
if (System.Web.HttpContext.Current != null)
userId = (from user in Users.Where(u => u.UserName == System.Web.HttpContext.Current.User.Identity.Name) select user.Id).SingleOrDefault();
var modifiedBidEntries = ChangeTracker.Entries<User>()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
foreach (EntityEntry<User> entry in modifiedBidEntries)
{
entry.Property("Modified").CurrentValue = DateTime.UtcNow;
entry.Property("ModifiedById").CurrentValue = userId;
if (entry.State == EntityState.Added)
{
entry.Property("Created").CurrentValue = DateTime.UtcNow;
entry.Property("CreatedById").CurrentValue = userId;
}
}
return base.SaveChanges();
}
Final Thoughts:
For implementing something like audit columns, I'll take the Shadow Properties approach since these are cross cutting concerns and do not necessarily belong to my domain objects so having them implemented this way will keep my domain objects nice and clean.
I've worked on a library that might help.
Take a look at Audit.EntityFramework library, it intercepts SaveChanges() and is compatible with EF Core versions.

Filter/Search using Multiple Fields - ASP.NET MVC

I am using ASP.NET MVC with EF 6.
I have a stock page which shows all the information on stock items. Now I want to filter records too.
In picture below I have 3 options. I might filter by each option, one at a time or by combination of two or with all three.
I was thinking of writing linq query for each and every options selected. But this wouldn't be possible if filter option increases.Is there is any better way to this.
Thanks!
This is what I did in my controller.(currently dropdown has two options, excluding : " -- select one -- ")
public ActionResult StockLevel(string option, string batch, string name)
{
if (option != "0" && batch == "" && name == "")
{
if(option == "BelowMin")
{
List<Stock> stk = (from s in db.Stocks
where s.Qty < s.Item.AlertQty
select s).ToList();
return View(stk);
}
else
{
List<Stock> stk = (from s in db.Stocks
where s.Qty == s.InitialQty
select s).ToList();
return View(stk);
}
}
if (option == "0" && batch != "" && name == "")
{
List<Stock> stk = (from s in db.Stocks
where s.BatchNo == batch
select s).ToList();
return View(stk);
}
if (option == "0" && batch == "" && name != "")
{
List<Stock> stk = (from s in db.Stocks
where s.Item.Name.StartsWith(""+name+"")
select s).ToList();
return View(stk);
}
return View(db.Stocks.ToList());
}
I recommend you separate concerns and use an approach that the code in your controller be like this, simple, beautiful and extensible:
public ActionResult Index(ProductSearchModel searchModel)
{
var business = new ProductBusinessLogic();
var model = business.GetProducts(searchModel);
return View(model);
}
Benefits:
You can put anything you need in your ProductSearchModel based on your requirements.
You can write any logic in GetProducts based on requirements. There is no limitation.
If you add a new field or option to search, your action and controller will remain untouched.
If the logic of your search changes, your action and controller will remain untouched.
You can reuse logic of search wherever you need to search on products, in controllers or even in other business logic.
Having such ProductSearchModel, you can use it as model of ProductSearch partial view and you can apply DataAnnotations to it to enhance the model validation and help UI to render it using Display or other attributes.
You can add other business logic related to your product in that business logic class.
Following this way you can have a more organized application.
Sample Implementation:
Suppose you have a Product class:
public class Product
{
public int Id { get; set; }
public int Price { get; set; }
public string Name { get; set; }
}
You can create a ProductSearchModel class and put some fields you want to search based on them:
public class ProductSearchModel
{
public int? Id { get; set; }
public int? PriceFrom { get; set; }
public int? PriceTo { get; set; }
public string Name { get; set; }
}
Then you can put your search logic in ProductBusinessLogic class this way:
public class ProductBusinessLogic
{
private YourDbContext Context;
public ProductBusinessLogic()
{
Context = new YourDbContext();
}
public IQueryable<Product> GetProducts(ProductSearchModel searchModel)
{
var result = Context.Products.AsQueryable();
if (searchModel != null)
{
if (searchModel.Id.HasValue)
result = result.Where(x => x.Id == searchModel.Id);
if (!string.IsNullOrEmpty(searchModel.Name))
result = result.Where(x => x.Name.Contains(searchModel.Name));
if (searchModel.PriceFrom.HasValue)
result = result.Where(x => x.Price >= searchModel.PriceFrom);
if (searchModel.PriceTo.HasValue)
result = result.Where(x => x.Price <= searchModel.PriceTo);
}
return result;
}
}
Then in your ProductController you can use this way:
public ActionResult Index(ProductSearchModel searchModel)
{
var business = new ProductBusinessLogic();
var model = business.GetProducts(searchModel);
return View(model);
}
Important Note:
In a real world implementation, please consider implementing a suitable Dispose pattern for your business class to dispose db context when needed. For more information take a look at Implementing a Dispose method or Dispose Pattern.
Conditional filtering
.ToList(), .First(), .Count() and a few other methods execute the final LINQ query. But before it is executed you can apply filters just like that:
var stocks = context.Stocks.AsQueryable();
if (batchNumber != null) stocks = stocks.Where(s => s.Number = batchNumber);
if (name != null) stocks = stocks.Where(s => s.Name.StartsWith(name));
var result = stocks.ToList(); // execute query
WhereIf LINQ Extension
Simple WhereIf can significantly simplify code:
var result = db.Stocks
.WhereIf(batchNumber != null, s => s.Number == batchNumber)
.WhereIf(name != null, s => s.Name.StartsWith(name))
.ToList();
WhereIf implementation. It's a simple extension method for IQueryable:
public static class CollectionExtensions
{
public static IQueryable<TSource> WhereIf<TSource>(
this IQueryable<TSource> source,
bool condition,
Expression<Func<TSource, bool>> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
}
Non-WhereIf LINQ way (Recommended)
WhereIf provides more declarative way, if you don't want to use extensions you can just filter like that:
var result = context.Stocks
.Where(batchNumber == null || stock.Number == batchNumber)
.Where(name == null || s => s.Name.StartsWith(name))
.ToList();
It gives an exact same effect as WhereIf and it will work faster as runtime will need to build just one ExpressionTree instead of building multiple trees and merging them.
I've written some extensions to make this easier. https://www.nuget.org/packages/LinqConditionalExtensions/
It's not reinventing the wheel. Some of the extensions have already been recommended. You could rewrite your logic as follows.
var results = db.Stocks
.If(option != "0", stocks => stocks
.IfChain(option == "BelowMin", optionStocks => optionStocks
.Where(stock => stock.Qty < stock.Item.AlertQty))
.Else(optionStocks => optionStocks
.Where(stock => stock.Qty == stock.InitialQty)))
.WhereIf(!string.IsNullOrWhiteSpace(batch), stock => stock.BatchNo == batch)
.WhereIf(!string.IsNullOrWhiteSpace(name), stock => stock.Item.Name.StartsWith("" + name + ""))
.ToList();
return results;
Basically, the initial If() method will apply the passed if-chain if the condition is true. The IfChain() is your nested if-else statement. IfChain() allows you to chain multiple IfElse() and end with an Else().
The WhereIf() will just conditionally apply your where clause if the condition is true.
If you are interested in the library, https://github.com/xKloc/LinqConditionalExtensions has a readme.
public ActionResult Index(string searchid)
{
var personTables = db.PersonTables.Where(o => o.Name.StartsWith(searchid) )|| o.CombanyTable.ComName.StartsWith(searchid) ).Include(k => k.CombanyTable);
return View(personTables.ToList());
}

Entity framework Include - Can we have it condition based .Include(p => includeFiles == true ? p.FileMasters : null)

Is it possible to have Include work based on function parameter as used in code below. By going throug MSDN documentation for Include, looks like it's not possible.
public static Response<Person> GetById(int id, bool includeAddress = false
, bool includeFiles = false
, bool includeTags = false)
{
var output = new Response<Person>();
using (var dbContext = new SmartDataContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
output.Entity = dbContext.EntityMasters.OfType<Person>()
.Include(p => includeFiles == true ? p.FileMasters : null)
.Include(p => includeAddress == true ? p.Addresses : null)
.Include(p => includeTags == true ? p.Tags : null)
.FirstOrDefault(e => e.EntityId == id);
}
return output;
}
Is there any trick to handle it directly or I have to build expression instead. I am not sure even how would I build an expression for this rather checking in dbContext. I am thinking if I can build expression before entering dbContext scope.
What I am looking for is to have all the conditions resolved before jumping into USING statement. In example below I creating an expression and using it inside USING
public static Response<IEnumerable<ConfigurationType>> GetByAttributeType(int attributeType)
{
Response<IEnumerable<ConfigurationType>> output = new Response<IEnumerable<ConfigurationType>>();
System.Linq.Expressions.Expression<System.Func<ConfigurationType, bool>> expressions=null;
switch (attributeType)
{
case 1:
expressions = a => a.IsItemAttribute == true;
break;
case 2:
expressions = a => a.IsReadPointAttribute == true;
break;
default:
expressions = a => a.IsPersonAttribute == true;
break;
}
using (var context = new SmartDataContext())
{
context.Configuration.ProxyCreationEnabled = false;
output.Entity = context.ConfigurationTypes.Where(expressions).ToList();
}
return output;
}
Similarly what I am expecting is something like this. This sounds weird, just trying to overthink may be if there is a way to resolve p somehow.
IQueryable<Person> query = includeFiles?Include(p=>p.Files):null; //p is undefined
query.Append(includeTags?Include(p=>p.Tags):null);
I am not sure if it's possible or not. If not, please help me understand the reason.
Build your query up piece by piece, calling Include conditionally. If you want to perform this work using logic outside of the using statement (and / or outside of the method), you could pass a type that encapsulates the logic for applying the necessary Include statements.
For example:
public static Response<Person> GetById(int id, IIncludeConfiguration<Person> includeConfiguration = null)
{
var output = new Response<Person>();
using (var dbContext = new SmartDataContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
var query = dbContext.EntityMasters.OfType<Person>();
if(includeConfiguration != null)
{
query = includeConfiguration.ApplyIncludes(query);
}
output.Entity = query.FirstOrDefault(e => e.EntityId == id);
}
return output;
}
public interface IIncludeConfiguration<TEntity> where TEntity : class;
{
IQueryable<TEntity> ApplyIncludes(IQueryable<TEntity> query)
}
public class PersonIncludeConfiguration : IIncludeConfiguration<Person>
{
public bool IncludeFiles {get;set;}
public bool IncludeAddresses {get;set;}
....
public IQueryable<Person> ApplyIncludes(IQueryable<Person> query)
{
if(IncludeFiles) query = query.Include(x => x.FileMasters);
if(IncludeAddresses) query = query.Include(x => x.Addresses);
....
return query;
}
GetById(1234, new PersonIncludeConfiguration{IncludeFiles = true});
You can't do it that way but you can always build up the IQuerable on your own. Something like:
var queryable = dbContext.EntityMasters.OfType<Person>();
if (includeFiles)
{
queryable = queryable.Include(p => p.FileMasters);
}

Categories