I'm running with the code below and I can see that one key is created in the table [DataProtectionKeys]
services.AddDataProtection()
.SetApplicationName(dataProtectionSettings.ApplicationName)
.ProtectKeysWithCertificate(serviceCertificate)
.UnprotectKeysWithAnyCertificate(serviceCertificate)
.PersistKeysToDbContext<DataProtectionContext>();
I'm using this database context (am I missing something?):
class DataProtectionContext : DbContext, IDataProtectionKeyContext
{
/// <summary>
/// A recommended constructor overload when using EF Core with dependency injection.
/// </summary>
/// <param name="options"></param>
public DataProtectionContext(DbContextOptions<DataProtectionContext> options)
: base(options) { }
/// <summary>
/// This maps to the table that stores keys.
/// </summary>
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
But, if I change to ApplicationName string value to something else, I don't see that a new key is created
Any idea why or how to fix it so this database table can support multiple application
I want to store my entity classes' properties' values as extra if occurred any error. What is the best practises to do this ? I thought, getting values by reflection and write them to database is a good solution but then It creates another question. How can I reach all values including Collections' values, in other words children objects' values. I have tried to reach by using reflection but I stucked,failed. If my solution is wrong, I am open to any suitable solutions, suggestions :)
For example :
public class Author : BaseEntity
{
/// <summary>
/// Gets or sets a value indicating the author's name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets a value indicating the author's surname
/// </summary>
public string Surname { get; set; }
/// <summary>
/// Gets or sets a value indicating the author's birthdate
/// </summary>
public DateTime BirthDate { get; set; }
/// <summary>
/// Gets or sets a value indicating the author's death of date
/// </summary>
public DateTime DeathDate { get; set; }
/// <summary>
/// Gets or sets a value indicating rating entity
/// </summary>
public virtual AuthorRating Rating { get; set; }
/// <summary>
/// Gets or sets a value indicating idenitifier of the author's nationality
/// </summary>
public int NationalityId { get; set; }
/// <summary>
/// Gets or sets a value indicating the author's nationality
/// </summary>
public virtual Nation Nationality { get; set; }
/// <summary>
/// Gets or sets the collection of votes
/// </summary>
public virtual ICollection<AuthorVote> HavingVotes { get; set; }
/// <summary>
/// Gets or sets the collection of quotes which he/she said
/// </summary>
public virtual ICollection<Quote> OwnQuotes { get; set; }
/// <summary>
/// Gets or sets the collection of favourited rows by user
/// </summary>
public virtual ICollection<UserFavouriteAuthor> Favouriteds { get; set; }
}
For example, How can I reach Author.Qutes.QuoteVotes class and their values and their childrens recursively by using reflection ?
To handle a DbEntityValidationException, surround your DbContext related code in a try catch block and use this for your exception
public void AppendValidationErrors(Exception e)
{
DbEntityValidationException ex = e is DbEntityValidationException ?
e as DbEntityValidationException : null ;
if (ex == null) { log.Info(e); return; }
foreach (var eve in ex.EntityValidationErrors)
{
log.Info(string.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State));
foreach (var ve in eve.ValidationErrors)
{
log.Info(string.Format("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage));
}
}
}
To get the property values, you can use EF's DbPropertyValues
public static void PrintValues(DbPropertyValues values)
{
foreach (var propertyName in values.PropertyNames)
{
Console.WriteLine("Property {0} has value {1}",
propertyName, values[propertyName]);
}
}
PrintValues(context.Entry(User).GetDatabaseValues());
If you want to log your database's erroneous behavior, you can inhert from the Entity Framework'sIDbCommandInterceptor and intercept the database's warnings and errors.
/// <summary>
/// The supported logging types
/// </summary>
public enum LogTarget { Log4net, Console, File };
public class DbCommandLogger : IDbCommandInterceptor
{
#region Appender Definitions
/// <summary>
/// Assign a method to append an error
/// </summary>
/// <param name="error">The error to append</param>
private Action<string> appendError;
/// <summary>
/// Assign a method to append a warning
/// </summary>
/// <param name="warning">The warning to append</param>
private Action<string> appendWarning;
#endregion
#region Fields
private static readonly log4net.ILog log = log4net.LogManager.GetLogger("WarningsAndErrorsAppender");
#endregion
#region Construct and Setup
public DbCommandLogger(LogTarget logTarget, string path = "db")
{
SetupAppenders(logTarget, path);
}
/// <summary>
/// Setups appenders according to the specified log target. It can only be accessed via the constructor
/// </summary>
/// <param name="logTarget">The log target</param>
/// <param name="path">The file path. Leave empty if you don't want to specify a path</param>
private void SetupAppenders(LogTarget logTarget, string path = "db")
{
switch (logTarget)
{
case LogTarget.Console:
appendError = Console.Write;
appendWarning = Console.Write;
break;
case LogTarget.File:
appendError = File.CreateText(path + ".Errors.log").WriteLine;
appendWarning = File.CreateText(path + ".Warning.log").WriteLine;
break;
case LogTarget.Log4net:
appendWarning = x => log.Warn(x);
appendError = x => log.Error(x);
break;
default:
appendWarning = x => { };
appendError = x => { };
break;
}
}
#endregion
#region Queries
public void NonQueryExecuting(
DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
LogIfNonAsync(command, interceptionContext);
}
public void NonQueryExecuted(
DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
LogIfError(command, interceptionContext);
}
public void ReaderExecuting(
DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
LogIfNonAsync(command, interceptionContext);
}
public void ReaderExecuted(
DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
LogIfError(command, interceptionContext);
}
public void ScalarExecuting(
DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
LogIfNonAsync(command, interceptionContext);
}
public void ScalarExecuted(
DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
LogIfError(command, interceptionContext);
}
#endregion
#region Log Commands
/// <summary>
/// Log a warning for any command that is executed non-asynchronously
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="command">The command being executed.</param>
/// <param name="interceptionContext">Contextual information associated with the call.</param>
private void LogIfNonAsync<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
if (!interceptionContext.IsAsync)
{
appendWarning(String.Format("Non-async command used: {0}", command.CommandText));
}
}
/// <summary>
/// Log an error for any command that throws when executed
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="command">The command being executed.</param>
/// <param name="interceptionContext">Contextual information associated with the call.</param>
private void LogIfError<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
if (interceptionContext.Exception != null)
{
appendError(String.Format("Command {0} failed with exception {1}",
command.CommandText, interceptionContext.Exception));
}
}
#endregion
#region Helpers
[MethodImpl(MethodImplOptions.NoInlining)]
public string GetCurrentMethod()
{
StackTrace st = new StackTrace();
StackFrame sf = st.GetFrame(1);
return sf.GetMethod().Name;
}
#endregion
}
You can then register the interceptor using a DbContext extension method
/// <summary>
/// Logs erroneous database behavior. Set the appender method via the log4net.config file located in the project's target folder
/// </summary>
/// <param name="context">The DbContext object that's being tracked</param>
public static void LogWarningsAndErrors(this DbContext context,LogTarget logTarget)
{
string path = FolderExists("LogFiles") ? "LogFiles\\" + context.ToString() :context.ToString();
DbInterception.Add(new DbCommandLogger(logTarget, path));
}
Finally, you can easily log the errors via your DbContext like this:
context.LogWarningsAndErros(LogTarget.Log4Net);
I think this answer is the solution to my problem but I am struggling to understand how to apply it to my problem. Like the other post, I have a two collections I want to keep in sync. My model object has collections of strings:
public class Person {
public int PersonId {get; set; }
public string PersonName { get; set; }
public List<string> PersonNicknames { get; set; }
}
I wrap this model object in its own ViewModel (PersonViewModel). To allow the Nicknames to be edited I also wrap them in their own NicknameViewModel. PersonViewModel then exposes an ObservableCollection<NicknameViewModel> NicknameViewModelCollection which is populated at construction:
foreach (string stringItem in _person.PersonNicknames)
{
var nicknameViewModel = new NicknameViewModel(stringItem);
this.NicknameViewModelCollection.Add(nicknameViewModel);
}
When a string is added, removed or changed in PersonViewModel.NicknameViewModelCollection the change is not reflected in the Model collection (i.e. Person.Nicknames). Whenever the user modifies, edits or deletes the string item I need to update the Model collection. I don't understand how the linked answer works or how to apply it to this problem. An example would be amazing... I'm just at a loss here.
This is my standard solution for what you are searching for. It has a bit of overhead for your scenario, because it works with a ViewModel type that has a field for it's context, etc. Anyway, the sollution should become obvious. The collection syncs OneWayToSource in general and TwoWay if the model collection itself is observable. Does this help you? If not, please ask...
/// <summary>
/// Observable collection of ViewModels that pushes changes to a related collection of models
/// </summary>
/// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
/// <typeparam name="TModel">Type of models in underlying collection</typeparam>
public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
where TViewModel : class, IViewModel, new()
where TModel : class
{
private readonly object _context;
private readonly ICollection<TModel> _models;
private bool _synchDisabled;
/// <summary>
/// Constructor
/// </summary>
/// <param name="models">List of models to synch with</param>
/// <param name="context"></param>
/// <param name="autoFetch">
/// Determines whether the collection of ViewModels should be
/// fetched from the model collection on construction
/// </param>
public VmCollection(ICollection<TModel> models, object context = null, bool autoFetch = true)
{
_models = models;
_context = context;
// Register change handling for synchronization
// from ViewModels to Models
CollectionChanged += ViewModelCollectionChanged;
// If model collection is observable register change
// handling for synchronization from Models to ViewModels
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
// Fecth ViewModels
if (autoFetch) FetchFromModels();
}
/// <summary>
/// CollectionChanged event of the ViewModelCollection
/// </summary>
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
/// <summary>
/// Load VM collection from model collection
/// </summary>
public void FetchFromModels()
{
// Deactivate change pushing
_synchDisabled = true;
// Clear collection
Clear();
// Create and add new VM for each model
foreach (TModel model in _models)
AddForModel(model);
// Reactivate change pushing
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Return if synchronization is internally disabled
if (_synchDisabled) return;
// Disable synchronization
_synchDisabled = true;
// Synchronize collection of Models
if (e.NewItems != null)
foreach (var v in e.NewItems.OfType<IViewModel<TModel>>())
v.AddModelTo(_models);
if (e.OldItems != null)
foreach (var v in e.OldItems.OfType<IViewModel<TModel>>())
v.RemoveModelFrom(_models);
//Enable synchronization
_synchDisabled = false;
}
private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
// Synchronize collection of ViewModels
if (e.NewItems != null)
foreach (TModel m in e.NewItems.OfType<TModel>()) this.AddIfNotNull(CreateViewModel(m));
if (e.OldItems != null) foreach (TModel m in e.OldItems) this.RemoveIfContains(GetViewModelOfModel(m));
}
private TViewModel CreateViewModel(TModel model)
{
return ViewModelCache.Get<TViewModel>.ForExistingModel(model, _context);
}
private TViewModel GetViewModelOfModel(TModel model)
{
return Items.OfType<IViewModel<TModel>>().FirstOrDefault(v => v.IsViewModelOf(model)) as TViewModel;
}
/// <summary>
/// Adds a new ViewModel for the specified Model instance
/// </summary>
/// <param name="model">Model to create ViewModel for</param>
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
/// <summary>
/// Adds a new ViewModel with a new model instance of the specified type,
/// which is the ModelType or derived from the Model type
/// </summary>
/// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}
I have the following context:
public class DataContext : DbContext
{
/// <summary>
/// Gets or sets Addresses.
/// </summary>
public DbSet<Address> Addresses { get; set; }
/// <summary>
/// Gets or sets Users.
/// </summary>
public DbSet<Users> Users { get; set; }
}
I my application user may change data in say user data and then he may want to cancel changes. The best way to do this, is to refresh the DataContext from the database. But DbContext has no Refresh method. How can I refresh my DataContext?
You can reload the entity from the database as follows.
context.Entry(user).Reload();
Or you can try out the methods described in this question.
Class 1
public partial class Profile : BaseEntity
{
#region Properties
/// <summary>
/// Gets or sets the Profile identifier
/// </summary>
public int ProfileId { get; set; }
/// <summary>
/// Gets or sets the FullName
/// </summary>
public string FullName { get; set; }
/// <summary>
/// Gets or sets the Gender
/// </summary>
public string Gender { get; set; }
/// <summary>
/// Gets or sets the DateofBirth
/// </summary>
public DateTime DateofBirth { get; set; }
}
Class 2
public partial interface IProfileService
{
/// <summary>
/// Gets an Profile by profile identifier
/// </summary>
/// <param name="profileid">profile identifier</param>
/// <returns>Profile</returns>
Profile GetProfileById(int profileid);
/// <summary>
/// Gets all Profiles
/// </summary>
/// <returns>Profile collection</returns>
List<Profile> GetAllProfiles();
}
Class 3
public partial class ProfileService : IProfileService
{
/// <summary>
/// Gets a profile by profile identifier
/// </summary>
/// <param name="profileId">Profile identifier</param>
/// <returns>profile</returns>
public Profile GetProfileById(int profileid)
{
if (profileid == 0)
return null;
var query = from a in _context.Profilemasters
where a.ProfileId == profileid
select a;
var profile = query.SingleOrDefault();
return profile;
}
}
In Class 3 of return profile I get an error Message that "Cannot Implicitly Convert 'Data.Profilemaster' to 'Library.Profile'. Whareas Data.Profilemaster is an Entity table amd Library.Profile is Class ie. Class 1.
And when I use "MyContext.Profilemasters.AddObject(profile);" command to insert data I get another Error Message ie. "The Best Overloaded Method match for...."
Please help Me.
I am New in Entrity Framework.
Anybody has the Idea to solve this
Try Like This .........
public ModelTest GetTest(int id)
{
ModelTest ob = new ModelTest();
var a = from r in _QEntities.Tests
where r.id == id
select r;
foreach (var item in a)
{
ob.TestName = item.name;
ob.TotalMarks = item.totalmarks;
}
return ob;
}