I know is a simple problem, but I don't find the way to manage this properly. I think it's ugly, and I'm sure a better way exists.
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
if (!includeDocumentTypes && !includeDocumentDescriptions)
{
return await _context.DocumentCategories.ToListAsync();
}
else if (includeDocumentTypes && !includeDocumentDescriptions)
{
return await _context.DocumentCategories.Include(dc => dc.DocumentTypes).ToListAsync();
}
else
{
return await _context.DocumentCategories.Include(dc => dc.DocumentTypes).ThenInclude(dt => dt.DocumentDescriptions).ToListAsync();
}
}
The goal is to include nested datas from boolean flags.
More information about my class below :
public class DocumentCategoriesRepository : GenericRepository<DocumentCategory>, IDocumentCategoriesRepository
{
public DocumentCategoriesRepository(myDbContext context) : base(context)
{
}
// Here is my method
}
What about
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
var categories = _context.DocumentCategories.AsQueryable();
if (includeDocumentTypes)
{
categories = categories.Include(dc => dc.DocumentTypes);
}
if (includeDocumentDescriptions)
{
categories = categories
.Include(dc => dc.DocumentTypes)
.ThenInclude(dt => dt.DocumentDescriptions);
}
return await categories.ToListAsync();
}
This should work as including DocumentTypes multiple times is not a problem for EF and is handled as if it was included just once.
How about something like this, where you build up the query in distinct steps.
CAVEAT - This code hasn't been tested and may not work out of the box. The type of query would likely need some work
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
var query = _context.DocumentCategories;
if (includeDocumentTypes)
{
query = query.Include(dc => dc.DocumentTypes);
if (includeDocumentDescriptions)
{
query = query.ThenInclude(dt => dt.DocumentDescriptions);
}
}
return await query.ToListAsync()
}
You can make simple edits like below
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
var lstCategoires = _context.DocumentCategories;
if (includeDocumentTypes)
{
lstCategoires = includeDocumentDescriptions ? lstCategoires.Include(dc => dc.DocumentTypes).ThenInclude(dt => dt.DocumentDescriptions) :
lstCategoires = lstCategoires.Include(dc => dc.DocumentTypes);
}
return await lstCategoires.ToListAsync();
}
Related
Here is a piece of code, where I try to execute different async methods, that need to be executed in specific order (the await, and Task.WhenAll() parts).
//Some other tasks before
Task<bool> taskIfcQuantityArea = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcQuantityArea");
});
Task<bool> taskIfcQuantityLength = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcQuantityLength");
});
Task<bool> taskIfcSiUnit = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcSiUnit");
});
Task<bool> taskIfcPropertySingleValue = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcPropertySingleValue");
});
//uses IfcPerson, IfcOrganization
Task<bool> taskIfcPersonAndOrganization = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcPersonAndOrganization");
});
//uses IfcOrganization
Task<bool> taskIfcApplication = Task.Run(async () =>
{
await taskIfcSiUnit;
return addGroupStringToDictionary("IfcApplication");
});
//uses IfcSiUnit
Task<bool> taskIfcMeasureWithUnit = Task.Run(async () =>
{
await taskIfcSiUnit;
return addGroupStringToDictionary("IfcMeasureWithUnit");
});
//some other tasks after.
When I do that job synchronously, all works fine, but when I do it in async, I have some random errors. At every test, the errors come randomly.
The only thing I see that could go wrong, is they all execute the same function addGroupStringToDictionary.
Here is the function :
private bool addGroupStringToDictionary(string typeName)
{
//int processCount = await Task.Run<int>(() =>
//{
GroupedListStrings groupElt = this.listGrouppedStrings.FirstOrDefault(x => x.Type == typeName.ToUpper());
if (groupElt != null)
{
List<string> listStringInGroup = groupElt.ListStrings;
foreach (string line in listStringInGroup)
{
try
{
if(typeName== "IfcLocalPlacement($")
{
typeName = "IfcLocalPlacement";
}
var type = Type.GetType("Ifc."+typeName);
if (typeName == "IfcPropertySingleValue" || typeName == "IfcDirection" || typeName == "IfcSiUnit" || typeName == "IfcQuantityLength" || typeName == "IfcQuantityArea" || typeName == "IfcQuantityVolume" || typeName == "IfcQuantityWeight")
{
try
{
object instance = Activator.CreateInstance(type, line);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
else if (typeName == "IfcOpeningElement")
{
try
{
object instance = Activator.CreateInstance(type, line, this.listDictionaries, this.DictionaryBolts);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
else
{
try
{
object instance = Activator.CreateInstance(type, line, this.listDictionaries);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
}
catch
{
this.addError(line);
}
}
this.listGrouppedStrings.Remove(groupElt);
this.reportProgressImport();
}
//return 100;
//});
this.reportProgressImport();
return true;
}
The catch got 1-2 times over a bit more than 1 million lines.
At each test the errors come randomly.
Is it possible that running the function simultaneously from several async methods, this is what causes the problem?
Here is the addToListDictionary function :
private void addToListDictionary(IfcElement elt)
{
if(elt.ErrorFound)
{
this.listReadButError.Add(elt);
return;
}
string type = elt.GetType().ToString();
if (elt is IfcRepere)
{
type = "Ifc.IfcRepere";
}
else if (elt is IfcRepereType)
{
type = "Ifc.IfcRepereType";
}
else if (elt is IfcPhysicalSimpleQuantity)
{
type = "Ifc.IfcPhysicalSimpleQuantity";
}
else if (elt is IfcProfileDef)
{
type = "Ifc.IfcProfileDef";
}
else if (elt is IfcGeometricRepresentationContext)
{
type = "Ifc.IfcGeometricRepresentationContext";
}
GroupDictionary group = this.ListDictionaries.FirstOrDefault(x => x.Name == type);
if(group==null)
{
group = new GroupDictionary { Name = type };
this.ListDictionaries.Add(group);
}
group.ListElements[elt.ID] = elt;
if (elt is IfcMechanicalFastener)
{
IfcMechanicalFastener bolt = (IfcMechanicalFastener)elt;
this.DictionaryBolts[bolt.Tag] = bolt;
}
else if(elt is IfcProject)
{
this.listProjects.Add((IfcProject)elt);
}
else if(elt is IfcElementAssembly ifcAss)
{
this.DictionaryIfcElementAssemblies[ifcAss.Key] = ifcAss;
}
}
Also some additive information about my ListDictionaries :
private List<GroupDictionary> listDictionaries = new List<GroupDictionary>();
public List<GroupDictionary> ListDictionaries { get { return this.listDictionaries; } set { this.listDictionaries = value; } }
And the class GroupDictionary
public class GroupDictionary
{
string name { get; set; }
public string Name { get { return this.name; } set { this.name = value; } }
public ConcurrentDictionary<int, IfcElement> ListElements = new ConcurrentDictionary<int, IfcElement>();
public GroupDictionary()
{
}
}
I made different GroupDictionary because as soon as I don't need one of them, I delete it to free space.
I have one dictionary with IfcPoint, I need it to gt IfcPolyLine (lines), but when I finish to treat all objects using IfcPoint, I clear remove the corresponding GroupDictionary in order to free some memory.
You have a obvious thread-safety issues here (i.e. you are trying to perform some operation which is not thread safe from multiple threads at a time). There are multiple ways you can try tackling it - using locks, or some synchronization primitives.
But in this case it seems that major source of issues is working with standard collections from multiple threads, which is not thread-safe (because thread-safety usually comes with performance price and is not always needed). You can start from switching to appropriate collections from System.Collections.Concurrent namespace.
To go down deeper I recommend free e-book by Joseph Albahari Threading in C#.
I have a method that takes a context and entity object as parameters.
This method should be able to determine if a common property (COID in my code) of any class(table) has a value.
I can't find a way to rewrite this code so it is more generic, and at the moment I am checking the type of each entity passed to the method.
public async static Task<bool> IsCOIDAssigned(ProjectEntities _context, object _entity)
{
var bSuccess = false;
//First type to check
if (_entity is tblLine)
{
var _line = _entity as tblLine;
await _context.Entry(_entity).ReloadAsync().ContinueWith(x =>
{
if (!x.IsFaulted)
{
var query = from c in _context.tblLines
where c.ID.Equals(_line.ID)
select c;
if(query.Single().COID.GetValueOrDefault() == 0)
{
Console.WriteLine("Not assigned");
bSuccess = true;
}
else
{
Console.WriteLine("Assigned");
bSuccess = false;
}
}
else
{
bSuccess = false;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
};
//Second type to check
if (_entity is tblDevice)
{
var _device = _entity as tblDevice;
await _context.Entry(_entity).ReloadAsync().ContinueWith(x =>
{
if (!x.IsFaulted)
{
var query = from c in _context.tblDevices
where c.ID.Equals(_device.ID)
select c;
if (query.Single().COID.GetValueOrDefault() == 0)
{
Console.WriteLine("Not assigned");
bSuccess = true;
}
else
{
Console.WriteLine("Assigned");
bSuccess = false;
}
}
else
{
bSuccess = false;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
};
//Third type to check ....
//Fourth type to check ....
return bSuccess;
}
Does anyone see a better solution to the problem?
I followed arekzyla's solution to make my tables inheret tblBase.
However, since I have a database-first model it was not possible to modify my model through code (not sure if this is possible?)
I managed to get what I was looking for thanks to arekzyla, but with less linq queries involved, I am casting my entities as TblBase which is a class containing ID and COID (common table properties.)
public static async Task<bool> IsEntityCheckedOut(ProjectEntities _context, object _entity)
{
var bCheckedOut = false;
await _context.Entry(_entity).ReloadAsync().ContinueWith(x =>
{
if (!x.IsFaulted)
{
var _baseentity = _entity as TblBase;
if (_baseentity.COID.GetValueOrDefault() != 0)
{
Console.WriteLine("Assigned");
bCheckedOut = true;
}
else
{
Console.WriteLine("Not Assigned");
}
}
}, TaskScheduler.FromCurrentSynchronizationContext());
return bCheckedOut;
}
You can make base abstract class for both tblLine and tblDevice containing at least properties COID and Id.
public abstract class TblBase
{
public int Id { get; set; }
public int COID { get; set; }
//other common properties
}
public class TblLine : TblBase
{
// properties
}
public class TblDevice : TblBase
{
// properties
}
So in your context you will have:
public DbSet<TblBase> TblBases { get; set; }
You have to set inheritance to TPC (Table-per-Concrete-Type) for both classes:
modelBuilder.Entity<TblLine>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("tblLines");
});
modelBuilder.Entity<TblDevice>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("tblDevices");
});
Then you can use context.TblBases to query data.
I have a few items -- let's just call them itemA, itemB, itemC, and itemD -- and I would like to validate each of them using a validation method I have written.
The validation method return type and signature is as follows:
public async Task<ValidationMessage> ValidateItem(MyClass item);
The ValidationMessage is a simple class:
public class ValidationMessage
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
}
To validate each item, currently I have the following code:
ValidationMessage result = new ValidationMessage();
result = await this.ValidateItem(itemA);
if (!result.Success)
{
return result;
}
result = await this.ValidateItem(itemB);
if (!result.Success)
{
return result;
}
result = await this.ValidateItem(itemC);
if (!result.Success)
{
return result;
}
result = await this.ValidateItem(itemD);
return result;
As you can see, as soon as one of the items fails the validation method (meaning result.Success == false), I return and do not continue with validating the rest of them.
I think it's kind of tedious/ugly to have the repeated assignments to result and the repeated if statements. I was hoping there is some existing c# class/construct (perhaps LINQ can help) to write this more concisely. I made up the following to demonstrate what I'm sort of thinking:
ValidationMessage result = new ValidationMessage();
result = await this.ValidateItem(itemA).ContinueConditional(
(r => r.Success) => await this.ValidateItem(itemB).ContinueConditional(
(r => r.Success) => await this.ValidateItem(itemC).ContinueConditional(
(r => r.Success) => await this.ValidateItem(itemD))));
return result;
Basically, the return value of this.ValidateItem(itemA) is assigned to result and then this value goes into the ContinueConditional. If the result, r, has Success == true, then continue with validating itemB, and so on. If it's not successful, then exit out of that code and go straight to the return statement at the bottom, thus returning the item which failed the validation. If all items are validated then it will go to that return statement anyway.
I apologize for the long wall of text, especially if the c# construct already exists and is obvious. I appreciate any help with this.
Note: I have simplified my real example for the sake of posting this. One important part of the simplification is that itemA, itemB, itemC, and itemD are not the same type, despite referring to them as MyClass in the method signature above. So I can't just put them in a list and use LINQ. This also means I actually have different validation methods to accept the various items I have, but I didn't want to explain all that at the top in case it over-complicates things. The important part is that they all return ValidationMessage.
What about inverting the logic around the check for success/failure?
ValidationMessage result = await this.ValidateItem(itemA);
if (result.Success) result = await this.ValidateItem(itemB);
if (result.Success) result = await this.ValidateItem(itemC);
if (result.Success) result = await this.ValidateItem(itemD);
return result;
How about this? You can extend it to as many elements as you want.
var validationRules = new List<Func<Task<bool>>>(){
() => ValidateItem(itemA),
() => ValidateItem(itemB),
() => ValidateItem(itemC),
() => ValidateItem(itemD),
};
ValidationMessage result = new ValidationMessage();
foreach(var validationRule in validationRules)
{
result = await validationRule();
if(!result)
return result;
}
return result;
You could use FluentValidation to create a custom rule to loop through a list of models to validate. This is more overhead than the other answers but it's a good approach with clean readable code.
First define your shared model
public class YourSharedModel()
{
List<MyClass> Models = new List<MyClass>();
}
Define the validator:
public class SharedModelValidator : AbstractValidator<YourSharedModel>
{
public SharedModelValidator()
{
CustomRule(BeValid)
}
public bool BeValid(ValidationErrors<YourSharedModel> validationFailures, YourSharedModel sharedModel, ValidationContext<YourSharedModel> validationContext)
{
for (var m in sharedModel.Models)
{
var result = YourValidationMethod(m);
if (!result.Success)
{
validationFailures.AddFailureFor(x => m, result.ErrorMessage);
return false;
}
}
return true;
}
private YourValidationMethod(MyClass model)
{
// the code you have that actually validates
}
}
Implementation would be something like:
var yourSharedModel = new YourSharedModel();
yourSharedModel.Models.Add(itemA);
yourSharedModel.Models.Add(itemB);
// etc
var validator = new SharedModelValidator();
var results = validator.Validate(yourSharedModel);
if (!results.IsValid)
{
// do something with results.Errors
}
You could also keep all your errors and return false after the loop so that you get all the errors at once rather than one by one on submission, of course that's assuming a form post from a web application. Makes for a better user friendly experience.
You could use an interface and then add your items to a list that you foreach through:
ValidationMessage Test()
{
List<IValidatable> items = new List<IValidatable>();
MyClass1 item1 = new MyClass1();
MyClass2 item2 = new MyClass2();
items.Add(item1);
items.Add(item2);
ValidationMessage result = null;
foreach (var i in items)
{
result = i.ValidateItem();
if (!result.Success) break;
}
return result;
}
interface IValidatable
{
ValidationMessage ValidateItem();
}
public class ValidationMessage
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
}
public class MyClass1 : IValidatable
{
public ValidationMessage ValidateItem()
{
return new ValidationMessage();
}
}
public class MyClass2 : IValidatable
{
public ValidationMessage ValidateItem()
{
return new ValidationMessage();
}
}
Another (perhaps anorthodox) way to do it is to overload the && operator on ValidationMessage:
public class ValidationMessage
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
public static ValidationMessage operator &(ValidationMessage message1, ValidationMessage message2)
{
return message1.Success ? message2 : message1;
}
public static ValidationMessage operator |(ValidationMessage message1, ValidationMessage message2)
{
return message1.Success ? message1 : message2;
}
public static bool operator true(ValidationMessage message)
{
return message.Success;
}
public static bool operator false(ValidationMessage message)
{
return !message.Success;
}
}
Then you can do:
return (await this.ValidateItem(ItemA)) &&
(await this.ValidateItem(ItemB)) &&
(await this.ValidateItem(ItemC)) &&
(await this.ValidateItem(ItemD));
I have a WPF application.
The data is brought from the repository to the ViewModel. What would be the better way to retrieve the data:
Method 1:
In Repository:
public List<LogDetail> GetLogsOfTypeForCase(int caseId, LoggType logType)
{
using (var ctx = new SidraEntitiesNoChangesDetection())
{
var logs = (from l in ctx.Loggs
where l.log_fk_caseid == caseId && l.log_operation == logType.ToString()
select new LogDetail()
{
ColumnName = l.log_columnname,
DateAndTime = l.log_dateandtime,
IdentificationDetail = l.log_identificationDetail,
NewValue = l.log_new_value,
OldValue = l.log_old_value,
TableName = l.log_tablename,
UserCode = l.User.usr_code
}).ToList();
return logs;
}
}
In ViewModel:
await Task.Run(
() =>
{
if (false == this.CancellationTokenSource.IsCancellationRequested)
{
this.CaseLogs = this.dataAdapter.GetLogsOfTypeForCase(this.CaseId, LoggType.S);
}
},
this.CancellationTokenSource.Token
);
or
Method 2
In Repository:
public async Task<List<LogDetail>> GetLogsOfTypeForCase(int caseId, LoggType logType)
{
using (var ctx = new SidraEntitiesNoChangesDetection())
{
var logs = await (from l in ctx.Loggs
where l.log_fk_caseid == caseId && l.log_operation == logType.ToString()
select new LogDetail()
{
ColumnName = l.log_columnname,
DateAndTime = l.log_dateandtime,
IdentificationDetail = l.log_identificationDetail,
NewValue = l.log_new_value,
OldValue = l.log_old_value,
TableName = l.log_tablename,
UserCode = l.User.usr_code
}).ToListAsync();
return logs;
}
}
and in ViewModel
protected override async void Load()
{
if (false == this.CancellationTokenSource.IsCancellationRequested)
{
this.CaseLogs = await this.dataAdapter.GetLogsOfTypeForCase(this.CaseId, LoggType.S);
}
}
From what I have read, Method 1 would be preferred, but what be the advantages?
Method 2 is preferable, because it uses one less thread.
It also can (with some modifications) properly support cancellation:
public async Task<List<LogDetail>> GetLogsOfTypeForCase(int caseId, LoggType logType, CancellationToken token)
{
...
}).ToListAsync(token);
...
}
protected override async void Load()
{
this.CaseLogs = await this.dataAdapter.GetLogsOfTypeForCase(this.CaseId, LoggType.S, this.CancellationTokenSource.Token);
}
Method 2. The Async versions of these methods do all the "heavy lifting" for you, that was what they were designed for. Task.Run() is a heavy process call, you are required to handle all the potential errors and failures yourself, you don't need a sledge hammer here, just a light weight finishing hammer.
By this I mean that you are trying to create what the framework has already done for you, this is where Async calls were meant to be used so why not just use them?
We are using entity framework codefirst approach
I am new to entity framework and I am facing error while trying to do "New transaction is not allowed because there are other threads running in the session.
public class DatabaseBackup : IDataBackup
{
private readonly IMonarchDbContext m_db;
public DatabaseBackup(IMonarchDbContext podb)
{
if (podb == null)
throw new ArgumentNullException("podb");
m_db = podb;
}
public DBBackupHistory GetLatestBackupHistory(DBBackupFrequency backupFrequency = DBBackupFrequency.Periodic)
{
DBBackupHistory result = null;
// get the backup history of the given backuptype and populate the objects
var configId = m_db.DBBackupConfigurations.Where(c => c.ScheduleType == (int)backupFrequency && c.BackupStatus == 1).Distinct().Select(c => c.ConfigurationId).DefaultIfEmpty(-1).First();
if (configId > 0)
{
result = m_db.DBBackupHistorys.Where(b => b.Status == 1 && b.ConfigurationId == configId).OrderByDescending(lb => lb.BackupDatetime).FirstOrDefault();
}
return result;
}
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId);
return result;
}
public void Backup(DBBackupConfiguration config, int fileIndex)
{
Console.WriteLine("Running DB Backup type {0} to device {1}", (DBBackupType)config.BackupType, fileIndex);
m_db.StoredProc.SPBackup(config, fileIndex);
}
I am calling the below methods in another class as follows
private readonly IDataBackup m_dataBackup;
public int PerformBackup(int defaultPollIntervalInMinutes = 15)
{
// polling interval in Minutes
int pollInterval = defaultPollIntervalInMinutes;
int fileIndex = getCurrentDumpFileIndex();
// check for the backup configuration
var configurations = m_dataBackup.GetAllConfiguration();
foreach (var config in configurations)
{
var lastBackup = m_dataBackup.GetLatestBackupHistory(DBBackupFrequency.Weekly);
if (lastBackup == null)
{
m_dataBackup.Backup(config, fileIndex + 1);
break;
}
Here is the Db Context class is as below
public class MonarchDbContext:DbContext,IMonarchDbContext
{
private IStoredProcedure m_storedProc;
private static object m_dbIntializerSet;
public MonarchDbContext(string nameOrConnectionString)
: base( nameOrConnectionString )
{
//-- Set the DB initializer only once.
System.Threading.LazyInitializer.EnsureInitialized( ref m_dbIntializerSet,()=>{
Database.SetInitializer<MonarchDbContext>(null);
//-- Give debug builds a chance to overwrite the above.
_SetInitializerForDebugBuilds();
return new object();
});
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
var csb = new SqlConnectionStringBuilder( this.Database.Connection.ConnectionString );
csb.MultipleActiveResultSets = true;
this.Database.Connection.ConnectionString = csb.ToString();
var objectContext = ( this as IObjectContextAdapter ).ObjectContext;
objectContext.CommandTimeout = 3600;
}
#region Public "Tables"
public IDbSet<DBBackupConfiguration> DBBackupConfigurations { get; set; }
public IDbSet<DBBackupHistory> DBBackupHistorys { get; set; }
public IStoredProcedure StoredProc
{
get
{
return System.Threading.LazyInitializer.EnsureInitialized(ref m_storedProc, () => new BackupStoredProc(this.Database));
}
}
#endregion
please let me know how can i solve the issue.
I found the issue
I need to add toList() at the end of the Linq code and it just worked for me.
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId).ToList();
return result;
}
Just add the List to Ienumerbale types