I'm relatively new to EntityFramework (started using it today!)
I have the following code:
public class DemoContext : DbContext
{
public DemoContext() : base("demoContext")
{
}
public DbSet<BaseUser> Users {get; set;}
public DbSet<BaseSession> Sessions {get;set;}
}
public class DemoInitializer : DropCreateDatabaseAlways<DemoContext>
{
protected override void Seed(DemoContext context)
{
var staffUsers = new List<StaffUser>
{
new StaffUser {Id=1,Username="test",Password="test",DisplayName="test user",AccessFlags=(AccessFlags)2048 },
};
staffUsers.ForEach(su => context.Users.Add(su));
context.SaveChanges();
}
}
But whenever the context.SaveChanges(); line is called, it throws the following exception:
An exception of type
'System.Data.Entity.Infrastructure.DbUpdateException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: Error retrieving values from ObjectStateEntry.
See inner exception for details.
And the inner exception is:
The given key was not present in the dictionary.
Whilst this error doesn't really mean much to me at this point, and Googling around hasn't produced any answers, I have seemingly managed to nail it down to being related to the public property DbSet<BaseSession> Sessions {get;set} on the DemoContext class
(By this I mean, if I comment it out, the error doesn't occur and away we go!)
Why is this, and what's the correct fix?
Do I need to do something with all of the DbSet properties present in a DbContext if I use Initializer.Seed()?
I'm assuming you don't actually have to populate them all with data, as that doesn't make sense?
.
Screenshot of the full exception, incase it's useful!
.
EDIT
I've dialed it down to being a problem with this specific implementation of the BaseSession class:
public class UniqueSession : BaseSession
{
public Date DateOfSession { get; set; }
public string Comments { get; set; }
}
Which, as you can see, uses a custom Date class, which I pilfered from another question on here:
public class Date : IEquatable<Date>, IEquatable<DateTime>
{
public Date(DateTime date)
{
value = new DateTime(date.Date.Ticks, DateTimeKind.Unspecified);
//value = date.Date;
}
public bool Equals(Date other)
{
return other != null && value.Equals(other.value);
}
public bool Equals(DateTime other)
{
return value.Equals(other);
}
public override string ToString()
{
return value.ToString();
}
public static implicit operator DateTime(Date date)
{
return date.value;
}
public static explicit operator Date(DateTime dateTime)
{
return new Date(dateTime);
}
private DateTime value;
}
If I switch UniqueSession.DateOfSession to be a DateTime, this error doesn't occur?
.
EDIT 2
If I change the Date class so that the private DateTime value; is actually a public property (public DateTime value { get; set; }), the error goes away!
But why?
A complex type is (beyond other) a shortcut to "create columns in the database".
One column for one public property of the class. With no property in the class there is no column to map/create.
This may confuse EF.
The class Date for EF is like any other not built in (and handled by EF) type.
So:
- the DateOfSession is a reference to Date
- Date (Dates) is a table on SQL Server (and a DbSet on the context)
In this case the EF probably is looking for the DbSet<Date>
Related
I made some changes to add a field to a table, and now I'm getting an exception on casting.
I've narrowed down the code as much as I can and still get the error.
My changes have been completely removed and I'm still getting the issue.
One question is, if it really is of type ClientService as it claims in the exception, how can it have an issue with casting to IClientID? There are nothing but primitive types involved that I can see.
In this code
try {
using (CFDb db = CreateDbContext<CFDb>(context)) {
ServiceFilter serviceFilter = filters.Where(f => f.ClassName == "ServiceFilter").Select(f => f).FirstOrDefault() as ServiceFilter;
IQueryable<ClientService> query = BuildServiceFilter(serviceFilter, db);
query = BuildClientGovernmentAssistanceFilter(query, (ClientGovernmentAssistanceFilter)null, db);
ClientService[] result = query.ToArray();
}
} catch (Exception ex) {
Logger.LogException("GetServicesReportData", ex, System.Diagnostics.TraceEventType.Error);
return null;
}
query.ToArray() throws
System.NotSupportedException: Unable to cast the type 'Shared.ClientService' to type 'Shared.IClientID'. LINQ to Entities only supports casting EDM primitive or enumeration types.
BuildClientGovernmentAssistanceFilter looks like this
private IQueryable<T> BuildClientGovernmentAssistanceFilter<T>(IQueryable<T> query, ClientGovernmentAssistanceFilter filter, CFPINDb db) where T : IClientID {
query = from clientService in query
join clientGovernmentAssistance in db.ClientGovernmentAssistance on clientService.ClientID equals clientGovernmentAssistance.ClientID
select clientService;
return query;
}
If I comment out the join, or the call to the routine, the error goes away.
I don't think it is an issue with the joined table per se because I can substitute any other table that supports IClientID and get the same error.
Note that T here is IClientID and is called as ClientService.
I can get BuildServiceFilter down to this:
private IQueryable<ClientService> BuildServiceFilter(ServiceFilter serviceFilter, CFPINDb db) {
query = from clientService in db.ClientServices
select clientService;
return query;
}
Here are the definitions:
public interface IClientID {
int ClientID { get; set; }
}
[DataContract]
public class ClientService : IClientID {
[DataMember]
public int ClientID { get; set; }
[DataMember]
public int ServiceID { get; set; }
[DataMember]
public DateTime ServiceDate { get; set; }
}
ClientService is slightly modified:
public DbSet<ClientService> ClientServices { get; set; }
modelBuilder.Entity<ClientService>().HasKey(k => new { k.ClientID, k.ServiceID, k.ServiceDate });
modelBuilder.Entity<ClientService>().Property(p => p.ClientID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<ClientService>().Property(p => p.ClientID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
[DataContract]
public class ClientGovernmentAssistance: IClientID {
[DataMember, Key]
public int ClientGovernmentAssistanceID { get; set; }
[DataMember]
public int ClientID { get; set; }
}
It seems like it is something with the join, not with the table elements themselves, but I can't figure what.
If I pull the code out and inline the functions, it gives a different error about casting an anonymous type that makes even less sense.
The solution, as pointed out by Clay, was to add 'class' to all the IQueryable<T> function declarations.
private IQueryable<T> ... where T : class, IClientID
I didn't think this would work since it looked like all the cases where I saw the 'class' solution were going the other direction, trying to cast an interface to a class, whereas my error was casting a class to an interface. Looks like it works both ways.
I have a class called InstrumentConfigValues with properties that has type implementing an interface. Now I have an enum by name InstrumentConfig which has set of values. These values are like keys inside the json file. I want to map something like [JsonProperty(InstrumentConfig.LowDiskpace.ToString()].
For some reason its not allowing this and complains saying:
An attribute argument must be constant expression
I referred to many post specifically JsonStringEnumConverter. But how can I map each property with the enum key. I also saw this post JsonSerializationSettings but not able to correlate to my problem. Please help/
public class InstrumentConfigValues : IInstrumentConfig
{
public double SpaceNeededForSingleRun
{
get; set;
}
public int NumberOfInputSlots
{
get; set;
}
public int SupportedChannelCount
{
get; set;
}
}
//I want this inheritance as some other class wants to access the values.
public abstract class InstrumentConfigReadWrite : InstrumentConfigValues
{
protected ReturnCodes PopulateValuesFromJObject(JObject jObject, string path)
{
try
{
if (JsonConvert.DeserializeObject<InstrumentConfigValues>(jObject.ToString()) == null)
{
return ReturnCodes.ErrorReadingFile;
}
}
catch (JsonSerializationException jex)
{
SystemDebugLogLogger.LogException(jex, "Invalid Instrument Config File Values. Data needs to be copied over.");
return ReturnCodes.ErrorReadingFile;
}
return ReturnCodes.Success;
}
}
As long as you're using a current compiler, you can use nameof.
[JsonProperty(nameof(InstrumentConfig.LowDiskpace))]
If you try using this, and get an error like Compilation error: The name 'nameof' does not exist in the current context, that means you're not using a current compiler. The nameof keyword was introduced in C# 6.0/Visual Studio 2015--anything newer than that should be fine.
I recently upgraded Windows SmartClient solution from nHibernate 2.2 to 4.0 and am getting an exception when writing to the db.
The exception is thrown at this code:
this.session.Save(this.Location); // NHibernate.ISession
tx.Commit(); // exception thrown here
The exception is:
'System.InvalidCastException' in NHibernate.dll
System.InvalidCastException:
Unable to cast object of type 'System.Collections.ArrayList'
to type 'System.Collections.Generic.IEnumerable`1[System.Object]'.
There are several lists in the object being saved, here is a couple representative ones:
protected System.Collections.IList locationList;
public virtual System.Collections.IList AssociatedLocationList
{
get
{
if (this.locationList == null)
{
this.locationList = new System.Collections.ArrayList();
}
return this.locationList;
}
set { this.locationList = value; }
}
protected System.Collections.Generic.IList<Inspection> inspectionList;
public virtual System.Collections.Generic.IList<Inspection> InspectionList
{
get
{
if (this.inspectionList == null)
{
this.inspectionList = new System.Collections.Generic.List<Inspection>();
}
return this.inspectionList;
}
set { this.inspectionList = value; }
}
Note that some have a type specified and some don't.
One suggestion here is set the property to an IList, but I already have it as that.
What can be done?
Support for persistent non-generic collections was removed in NHibernate 4.0. Convert to generic collection instead.
See list of breaking changes in NHibernate 4.0 release notes.
The problem here could be with latest version of NHibernate ..i.e. 4.0
you have following options.
1) Most of the time, new version support backward compatibility. If it is the case, look for overload versions of iSession.Save method. You may get non-generic as well.
2) Possibly new save method only support generic type. Yours is non-generic i.e. ArrayList. If you can change it to Ilist<>, that should help.
3) If you do not have control on Ilis, then you may write converter in between which can convert your Arraylist to Ilist<> and it would work with Save method.
Hope this helps.
I hope I'm understanding your question right and if so, I'm not sure you need to be doing your null check at all. Instead your parent class should have a list of locations that are held in another table entirely.
This is class in which your location list is.
public class Parent
{
public virtual Guid Id { get; set; }
public virtual IList<Location> Locations { get; set; }
//This is the mapping for this class.
public class ParentMapping : ClassMap<Parent>
{
Id(x => x.Id).GeneratedBy.Guid();
//This is what relates your location list to this parent.
//Notice that in the Location object below,
//there's a Owner property which will point back to here.
HasMany(x => x.Locations).Cascade.All();
}
}
And this is the class that defines your locations.
public class Location
{
public virtual Guid Id { get; set; }
public virtual Parent Owner { get; set; }
public virtual string SomeProperty { get; set; }
//This is the mapping for this class.
public class LocationMapping : ClassMap<Location>
{
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.SomeProperty);
//This will relate our property back to the parent.
References(x => x.Owner);
}
}
I've read a bunch about auto implemented properties but I still don't quite get it. I have and entity:
public class News
{
public int NewsId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Date { get; set; }
}
Now I don't want the user to set date himself every time a new entity of News type is created. I want the record to be saved automatically with the datetime it's created. Thinking about it I suggest that it's enough to just modify the set for my property to something like :
public DateTime Date
{
get;
set
{
Date = DateTime.Now;
}
}
But reading about the topic I saw that the standard way is to create private variable and use it instead in the implementation. That's where I get a little bit lost.
private DateTime _date = null;
public DateTime Date
{
Well I'm not sure for the getter and setter implementations. It seems reasonable to have something like : set { _date = DateTime.Now;} and I have no idea how to deal with the get part since I want this data to be fetched from the database so something like : get {return _date;} doesn't make much sense to me even though almost every example with auto implementedset` returns the private variable. But I think that if the property is an entity this is not making a lot of sense.
Some ways to return the current date:
public DateTime Date { get { return DateTime.Now; } }
or
public class News
{
public News()
{
Date = DateTime.Now;
}
public DateTime Date { get; private set; }
}
The first one will always return the current date/time, even if that instance was created some time ago. The second one will return the date/time the instance was created. Both prevent the user from setting that Date value.
You could add a constructor to your class and then initialize there your property.
public class News
{
// properties goes here
public News()
{
Date=DateTime.Now;
}
}
A far better constructor would be the following
public News(int newsId, string title, string content)
{
NewsId=newsId;
Title=title;
Content=content;
Date=DateTime.Now;
}
That way you could create an object of type News in a single line of code.
News news = new News(1,"title1","whatever");
Don't touch the getter and setter! They are auto generated from a template and will be overridden every once and a while. Instead, as you might have noticed the generated entities are declared partially, create a partial class and declare a constructor there that sets the _date or Date of you r entity to DateTime.Now on construction (just as you desired).
public partial class News
{
public News()
{
this.Date = DateTime.Now;
}
}
I have a Sterling Service
public sealed class SterlingService : IApplicationService, IApplicationLifetimeAware, IDisposable
{
private SterlingEngine _engine;
private static readonly ISterlingDriver _driver = new IsolatedStorageDriver();
public static SterlingService Current { get; private set; }
public ISterlingDatabaseInstance Database { get; private set; }
public static void StartUpDatabase()
{
Current.Database = Current._engine.SterlingDatabase.RegisterDatabase<LocalDB>(_driver);
}
....
....
}
And My LocalDB Class where I have the table definitions is:
public class LocalDB : BaseDatabaseInstance
{
protected override List<ITableDefinition> RegisterTables()
{
return new List<ITableDefinition>()
{
CreateTableDefinition<ContactData, Guid>(k => k.UID.Value)
.WithIndex<ContactData, int, Guid>("CustomerId", t => t.CustomerId.Value),
CreateTableDefinition<ContactDetailData, Guid>(k => k.ContactData.UID.Value)
.WithIndex<ContactDetailData, int, Guid>("CustomerId", t => t.ContactData.CustomerId.Value),
....
};
}
}
Now the problem is when I get the data from storage.
Save works fine but when I fetch I get "Invalid cast operation exception from String to Guid".
public static List<ContactData> GetContactListFromLocalDB(int customerId)
{
var data = (from k in SterlingService.Current.Database.Query<ContactData, int, Guid>("CustomerId")
where k.LazyValue != null && k.Index == customerId
select k.LazyValue.Value);
return data.ToList<ContactData>(); (**HERE I GET THE EXCEPTION**)
}
Please let me know where I am doing wrong.
Thanks.
There might be a problem with you using the name CustomerId for more than one index.
Try changing the names of your indexes from CustomerId to something like ContactData_CustomerId and ContactDetailData_CustomerId (and similarly for any other indexes you haven't shown us).
I can't find anything in the Sterling documentation that suggests that names must be unique. Nonetheless, when I used Sterling, I gave all my indexes different names and I didn't have any such problems.
(As an aside, it might also be a good idea to move all your index names into string constants somewhere. That should help to avoid mistyping them, and you should also get IntelliSense support for them.)
http://sterling.codeplex.com/workitem/14343