I have this validator class:
internal class CustomerTypeValidator : AbstractValidator<CustomerType>
{
public CustomerTypeValidator()
{
RuleFor(x => x.Number).Must(BeANumber).WithState(x => CustomerTypeError.NoNumber);
}
private bool BeANumber(string number)
{
int temp;
bool ok = int.TryParse(number, out temp);
return ok && temp > 0;
}
}
And I have the service class:
public class CustomerTypeService
{
public CustomerType Save(CustomerType customerType)
{
ValidationResult results = Validate(customerType);
if (results != null && !results.IsValid)
{
throw new ValidationException<CustomerTypeError>(results.Errors);
}
//Save to DB here....
return customerType;
}
public bool IsNumberUnique(CustomerType customerType)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}
}
However I get the following exception:
Property name could not be automatically determined for expression x => x. Please specify either a custom property name by calling 'WithName'.
Is the above not the correct way to add an extra rule?
With the current version of FluentValidation, it is possible to solve the above problem by doing the following:
public bool IsNumberUnique(CustomerType customerType, int id)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Id).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}
Related
I'm using LinqKit with EntityFrameWorkCore to create projections with single element projections. But since some Models are nested I'm getting a stackoverflow. I tried to add a condition to the Projection method (bool mapCollusions) to prevent this, but it seems to be ignored. Does anyone have an idea how to prevent these circluar references?
public static Expression<Func<Transport, TransportModel>> GetMainProjection()
{
return transport => new TransportModel()
{
Inmate = transport.Inmate != null ? InmateProjectionsGetProjection(true).Invoke(transport.Inmate) : null,
};
public static Expression<Func<Inmate, InmateModel>> InmateProjectionsGetProjection(bool mapCollusions)
{
return inmate => new InmateModel()
{
Collusions = mapCollusions ? inmate.Collusions.AsQueryable()
.Select(collusion => CollusionProjectionsGetProjection(false).Invoke(collusion))
.ToList() : null
};
}
public static Expression<Func<Collusion, CollusionModel>> CollusionProjectionsGetProjection(bool mapInmate)
{
return collusion => new CollusionModel()
{
Inmate = mapInmate ? InmateProjectionsGetProjection(false).Invoke(collusion.Inmate) : null,
};
}
Try the following realisation:
public static Expression<Func<Transport, TransportModel>> GetMainProjection()
{
return transport => new TransportModel()
{
Inmate = transport.Inmate != null ? InmateProjectionsGetProjection(true).Invoke(transport.Inmate) : null,
};
}
public static Expression<Func<Inmate, InmateModel>> InmateProjectionsGetProjection(bool mapCollusions)
{
if (!mapCollusions)
return inmate => new InmateModel();
return inmate => new InmateModel()
{
Collusions = nmate.Collusions.AsQueryable()
.Select(collusion => CollusionProjectionsGetProjection(false).Invoke(collusion))
.ToList()
};
}
public static Expression<Func<Collusion, CollusionModel>> CollusionProjectionsGetProjection(bool mapInmate)
{
if (!mapInmate)
return collusion => new CollusionModel();
return collusion => new CollusionModel()
{
Inmate = InmateProjectionsGetProjection(false).Invoke(collusion.Inmate),
};
}
So, we created this project: https://github.com/efonsecab/BlazorRestaurant
The EF Core logic for SaveChanges is extended to automatically fill the data for the auditing columns.
Logic is in the BlazorRestaurantDbContext.partial.cs file.
public partial class BlazorRestaurantDbContext
{
public override int SaveChanges()
{
ValidateAndSetDefaults();
return base.SaveChanges();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
ValidateAndSetDefaults();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
ValidateAndSetDefaults();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ValidateAndSetDefaults();
return base.SaveChangesAsync(cancellationToken);
}
private void ValidateAndSetDefaults()
{
//Check https://www.bricelam.net/2016/12/13/validation-in-efcore.html
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
string ipAddresses = String.Empty;
string assemblyFullName = String.Empty;
string rowCretionUser = String.Empty;
if (entities.Any(p => p is IOriginatorInfo))
{
ipAddresses = String.Join(",", GetCurrentHostIPv4Addresses());
assemblyFullName = System.Reflection.Assembly.GetEntryAssembly().FullName;
if (Thread.CurrentPrincipal != null && Thread.CurrentPrincipal.Identity != null)
rowCretionUser = Thread.CurrentPrincipal.Identity.Name;
else
rowCretionUser = "Unknown";
}
foreach (var entity in entities)
{
if (entity is IOriginatorInfo)
{
IOriginatorInfo entityWithOriginator = entity as IOriginatorInfo;
if (String.IsNullOrWhiteSpace(entityWithOriginator.SourceApplication))
{
entityWithOriginator.SourceApplication = assemblyFullName;
}
if (String.IsNullOrWhiteSpace(entityWithOriginator.OriginatorIpaddress))
{
entityWithOriginator.OriginatorIpaddress = ipAddresses;
}
if (entityWithOriginator.RowCreationDateTime == DateTimeOffset.MinValue)
{
entityWithOriginator.RowCreationDateTime = DateTimeOffset.UtcNow;
}
if (String.IsNullOrWhiteSpace(entityWithOriginator.RowCreationUser))
{
try
{
entityWithOriginator.RowCreationUser = rowCretionUser;
}
catch (Exception)
{
entityWithOriginator.RowCreationUser = "Unknown";
}
}
}
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(
entity,
validationContext,
validateAllProperties: true);
}
}
public static List<string> GetCurrentHostIPv4Addresses()
{
//Check https://stackoverflow.com/questions/50386546/net-core-2-x-how-to-get-the-current-active-local-network-ipv4-address
// order interfaces by speed and filter out down and loopback
// take first of the remaining
var allUpInterfaces = NetworkInterface.GetAllNetworkInterfaces()
.OrderByDescending(c => c.Speed)
.Where(c => c.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
c.OperationalStatus == OperationalStatus.Up).ToList();
List<string> lstIps = new();
if (allUpInterfaces != null && allUpInterfaces.Count > 0)
{
foreach (var singleUpInterface in allUpInterfaces)
{
var props = singleUpInterface.GetIPProperties();
// get first IPV4 address assigned to this interface
var allIpV4Address = props.UnicastAddresses
.Where(c => c.Address.AddressFamily == AddressFamily.InterNetwork)
.Select(c => c.Address)
.ToList();
allIpV4Address.ForEach((IpV4Address) =>
{
lstIps.Add(IpV4Address.ToString());
});
}
}
return lstIps;
}
}
We have used this same code in other apps and works, great.
In this specific app, however, the System.Threading.Thread.CurrentPrincipal and ClaimsPrincipal.Current, are null, when the ValidateAndSetDefaults method is executed.
Even tried the following
services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.NameClaimType = "name";
options.Events.OnTokenValidated = (context) =>
{
System.Threading.Thread.CurrentPrincipal = context.Principal;
return Task.CompletedTask;
};
options.SaveToken = true;
});
The context.Principal does have all the data we need at this point, but we still get null reading the Thread's CurrenPrincipal object.
Any ideas how to fix it, so we can properly get the Authenticated User information in the code above, it exists in context.Principal, it is just not accesible later in the SaveChanges for EF Core.
Fixed by using Dependency Injection along with HttpContextAccesor
https://github.com/efonsecab/BlazorRestaurant/blob/main/src/BlazorRestaurantSln/BlazorRestaurant.DataAccess/Data/BlazorRestaurantDbContext.partial.cs
I have list with items and I check if an item exists in a List. If it exists, I try to find it.
I think that it has a little overhead, because I currently make two passes over the list. Is it possible to do in single pass?
Currently I have.
public partial class Item
{
public string text;
public int id;
}
....
static List<Item> data = new List<Item>();
static stub = new Item() { text = "NaN", id = -1 };
public static Item Get(int targetId)
{
if (data.Any(f => f.id == targetId) == false)
{
return stub;
}
return data.Find(f => f.id == targetId);
}
I want something like
...
public static Item Get(int targetId)
{
Item result;
result = data.Find(f => f.id == targetId);
if (result == null)
{
return stub;
}
return result;
}
You seem to be looking for FirstOrDefault():
Item _stub = new Item
{
text = "NaN",
id = -1
};
public Item FindByID(int id)
{
// Find the first item that has the provided id, or null if none exist.
var existingItem = data.FirstOrDefault(i => i.id == id);
// When not found, return the _stub
return existingItem ?? _stub;
}
You also may want to reconsider your naming conventions and whether you actually need these members to be static.
You can use List.FindIndex:
public static Item get(int i)
{
int index = data.FindIndex(item => item.id == i);
if (index == -1) return stub;
return data[index];
}
If it's actually an array you can use Array.FindIndex:
public static Item get(int i)
{
int index = Array.FindIndex(data, item => item.id == i);
if (index == -1) return stub;
return data[index];
}
So FirstOrDefault() is the way to go. You can also use SingleOrDefault() if there is only supposed to be one item in that list.
static stub = new Item() { text = "NaN", id = -1 };
public static Item get(int i)
{
Item result;
result = data.FirstOrDefault(f => f.id == i);
if (result == null)
{
return stub;
}
return result;
}
I use an extension method for exactly this purpose
public static T FirstOrSpecificDefault<T>(this IEnumerable<T> list,
Func<T, bool> predicate, T defaultValue)
{
return list.FirstOrDefault(predicate) ?? defaultValue;
}
usage in your case would be
Item result = list.FirstOrSpecificDefault(f => f.id == i, stub);
I think you can try this:
return data.Find(f => f.id == i) ?? stub;
I have a very similar function is only one previous report and the other future, how can I optimize and write beautiful?
public bool AnyPreviousReportByGroup(int groupID)
{
if(this.GroupID == groupID)
{
return true;
}
else
{
return PreviousReport.AnyPreviousReportByGroup(groupID);
}
}
public bool AnyNextReportByGroup(int groupID)
{
if (this.GroupID == groupID)
{
return true;
}
else
{
return NextReport.AnyNextReportByGroup(groupID);
}
}
The following code is a conciser way of achieving the same thing:
public bool AnyPreviousReportByGroup(int groupID)
{
return this.GroupID == groupID ||
this.PreviousReport != null &&
this.PreviousReport.AnyPreviousReportByGroup(groupID);
}
If you really want to use lambda expressions, here's a possible way:
public bool AnyReportByGroup(int groupID, Func<Report, Report> getOtherReport)
{
if (this.GroupID == groupID)
return true;
Report other = getOtherReport(this);
return other != null &&
other.AnyReportByGroup(groupID, getOtherReport);
}
You could then call this helper method using lambda expressions:
bool anyPrevious = this.AnyReportByGroup(groupID, report => report.PreviousReport);
bool anyNext = this.AnyReportByGroup(groupID, report => report.NextReport);
private bool AnyReportByGroup(int groupID, Func<int, bool> callback)
{
if (this.GroupID == groupID)
{
return true;
}
else
{
return callback(groupID);
}
}
public bool AnyPreviousReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, gid => PreviousReport.AnyPreviousReportByGroup(gid));
}
public bool AnyNextReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, gid => NextReport.AnyNextReportByGroup(gid));
}
But, I hope, that these methods are just a sample, and in your real code they're more complex.
Otherwise, I can't understand, what do you try to optimize.
May be like this
public enum ReportType{ Next, Previous }
public bool AnyReportByGroup(int groupID, ReportType type)
{
if(this.GroupID == groupID)
return true;
else
{
switch(type)
{
case ReportType.Next:
return NextReport.AnyNextReportByGroup(groupID);
case ReportType.Previous:
return NextReport.AnyPreviousReportByGroup(groupID);
}
return false;
}
}
Here's a non-recursive solution, assuming that you want to return false when you run out of reports:
public bool AnyPreviousReportByGroup(int groupID)
{
return GetEventualValue(groupID, r => r.PreviousReport);
}
public bool AnyNextReportByGroup(int groupID)
{
return GetEventualValue(groupID, r => r.NextReport);
}
public bool GetEventualValue(int groupID, Func<Report, Report> nextReport)
{
Report report = this;
while (report != null && report.GroupID != groupID)
{
report = nextReport(report);
}
return report != null;
}
Standard way to traverse linked lists is like so:
public bool AnyPreviousReportByGroup(int groupID)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = item.PreviousReport;
} while (item != null);
return false;
}
public bool AnyNextReportByGroup(int groupID)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = item.NextReport;
} while (item != null);
return false;
}
This has a benefit of not creating potentially massive call stacks the way a recusive approach would.
This also fixes your code, where it never returned false, it would just NPE.
Now we can refactor as you requested:
private bool AnyReportByGroup(int groupID, bool searchForward)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = searchForward ? item.NextReport : item.PreviousReport;
} while (item != null);
return false;
}
public bool AnyPreviousReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, false);
}
public bool AnyNextReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, true);
}
I have a LocalizedString classed used to store localization of a single value. The concept is loosely based on a post by Fabio Maulo.
I'm using the new Mapping-By-Code concept in NHibernate 3.2, but it seem to ignore the IUserType implementation, because when it generate the SQL, it create a column with a different name and with the default string NVARCHAR(255) type.
I'm trying to map this simple class
public class Region : Entity
{
/// <summary>
/// Initializes a new instance of the <see cref="Region"/> class.
/// </summary>
public Region()
{
}
/// <summary>
/// Gets or sets the localized name of the <see cref="Region"/>.
/// </summary>
public virtual LocalizedString Name { get; set; }
}
The resulting SQL is
create table Regions (RegionId INT not null, Item NVARCHAR(255) not null, primary key (RegionId))
The Item column here should be called Name and it should be of type XML. I presume the column name come from the name of the indexer of the LocalizedString.
This is my NHibernate configuration (its not complete, I'm in the process of building the convention)
private static Configuration CreateNHibernateConfiguration()
{
var cfg = new Configuration();
cfg.Proxy(p => p.ProxyFactoryFactory<NHibernate.Bytecode.DefaultProxyFactoryFactory>())
.DataBaseIntegration(db =>
{
db.ConnectionStringName = "***";
db.Dialect<MsSql2008Dialect>();
db.BatchSize = 500;
});
var mapper = new ConventionModelMapper();
var baseEntityType = typeof(Entity);
mapper.IsEntity((t, declared) => baseEntityType.IsAssignableFrom(t) && baseEntityType != t && !t.IsInterface);
mapper.IsRootEntity((t, declared) => baseEntityType.Equals(t.BaseType));
mapper.BeforeMapClass += (mi, t, map) =>
{
map.Table(Inflector.MakePlural(t.Name));
map.Id(x =>
{
x.Column(t.Name + "Id");
});
};
mapper.BeforeMapManyToOne += (insp, prop, map) =>
{
map.Column(prop.LocalMember.GetPropertyOrFieldType().Name + "Id");
map.Cascade(Cascade.Persist);
};
mapper.BeforeMapBag += (insp, prop, map) =>
{
map.Key(km => km.Column(prop.GetContainerEntity(insp).Name + "Id"));
map.Cascade(Cascade.All);
};
mapper.BeforeMapProperty += (insp, prop, map) =>
{
map.NotNullable(true);
};
var exportedTypes = baseEntityType.Assembly.GetExportedTypes();
mapper.AddMappings(exportedTypes.Where(t => t.Namespace.EndsWith("Mappings", StringComparison.Ordinal)));
var mapping = mapper.CompileMappingFor(exportedTypes.Where(t => t.Namespace.EndsWith("Data", StringComparison.Ordinal)));
cfg.AddDeserializedMapping(mapping, "MyModel");
SchemaMetadataUpdater.QuoteTableAndColumns(cfg);
return cfg;
}
This is the IUserType definition of my LocalizedString class:
/// <summary>
/// Defines a string that can have a different value in multiple cultures.
/// </summary>
public sealed partial class LocalizedString : IUserType
{
object IUserType.Assemble(object cached, object owner)
{
var value = cached as string;
if (value != null)
{
return LocalizedString.Parse(value);
}
return null;
}
object IUserType.DeepCopy(object value)
{
var toCopy = value as LocalizedString;
if (toCopy == null)
{
return null;
}
var localizedString = new LocalizedString();
foreach (var localizedValue in toCopy.localizedValues)
{
localizedString.localizedValues.Add(localizedValue.Key, localizedValue.Value);
}
return localizedString;
}
object IUserType.Disassemble(object value)
{
var localizedString = value as LocalizedString;
if (localizedString != null)
{
return localizedString.ToXml();
}
return null;
}
bool IUserType.Equals(object x, object y)
{
if (x == null && y == null)
{
return true;
}
if (x == null || y == null)
{
return false;
}
var localizedStringX = (LocalizedString)x;
var localizedStringY = (LocalizedString)y;
if (localizedStringX.localizedValues.Count() != localizedStringY.localizedValues.Count())
{
return false;
}
foreach (var value in localizedStringX.localizedValues)
{
if (!localizedStringY.localizedValues.ContainsKey(value.Key) || localizedStringY.localizedValues[value.Key] == value.Value)
{
return false;
}
}
return true;
}
int IUserType.GetHashCode(object x)
{
if (x == null)
{
throw new ArgumentNullException("x");
}
return x.GetHashCode();
}
bool IUserType.IsMutable
{
get { return true; }
}
object IUserType.NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
if (rs == null)
{
throw new ArgumentNullException("rs");
}
if (names == null)
{
throw new ArgumentNullException("names");
}
if (names.Length != 1)
{
throw new InvalidOperationException("names array has more than one element. can't handle this!");
}
var val = rs[names[0]] as string;
if (val != null)
{
return LocalizedString.Parse(val);
}
return null;
}
void IUserType.NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
if (cmd == null)
{
throw new ArgumentNullException("cmd");
}
var parameter = (DbParameter)cmd.Parameters[index];
var localizedString = value as LocalizedString;
if (localizedString == null)
{
parameter.Value = DBNull.Value;
}
else
{
parameter.Value = localizedString.ToXml();
}
}
object IUserType.Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
Type IUserType.ReturnedType
{
get { return typeof(LocalizedString); }
}
NHibernate.SqlTypes.SqlType[] IUserType.SqlTypes
{
get { return new[] { new XmlSqlType() }; }
}
}
You shouldn't use the IUserType in your domain model.
The IUserType interface should actually be called something like IUserTypeMapper, and you have to specify it explicitly in your mapping.
I suggest that you re-read that post.
Update: try this to map your type by convention:
mapper.BeforeMapProperty +=
(insp, prop, map) =>
{
if (/*determine if this is member should be mapped as LocalizedString*/)
map.Type<LocalizedString>();
};
Of course the "determine if..." part would be something you determine, like the property name starting with "Localized", a custom attribute, or anything you want.