I'm trying to create a simple database to use sqlce in a windows phone app.
I have a base class, and another set of classes that derive from it
Here's what i got
public abstract class EntityBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private int id;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int EntityId
{
get
{
return id;
}
set
{
if (id != value)
{
id = value;
OnPropertyChanged("Id");
}
}
}
}
[Table]
public class Derived : EntityBase
{
[Column]
public string Description
{
get;
set;
}
}
Then, I've got this class for datacontext purposes:
public class MyDataContext : DataContext
{
// Specify the connection string as a static, used in main page and app.xaml.
public static string DBConnectionString = "Data Source=isostore:/ToDo.sdf";
// Pass the connection string to the base class.
public MyDataContext(string connectionString)
: base(connectionString)
{ }
public Table<Derived> Deriveds;
}
And finally, here i'm trying to create the db :
private void Application_Launching(object sender, LaunchingEventArgs e)
{
using (MyDataContext db = new MyDataContext(MyDataContext.DBConnectionString))
{
if (db.DatabaseExists() == false)
{
//Create the database -> here's the error
db.CreateDatabase();
}
}
}
I'm getting the following error when trying to create the database :
Invalid column ID. [ EntityId ]
Yes, a very descriptive error message...
Any ideas on what's wrong? I've been tampering around with the attributes in the column but to no avail.
[EDIT] : for what i've been testing, if i put the EntityId property in the derived class, it doesn't crash. This can be 2 things.. one, that I'm missing something else in the base class, or the other one, that the column attribute for a primary key must belong to the class, and can't belong to the parent (which would be a extremely horrible design decision, we can't use inheritance???). if someone can confirm this that would be appreciated
Ok, i think i found what was happening. In this implementation of SQLCe, we need to do the following approach when using inheritance :
http://msdn.microsoft.com/en-us/library/bb399352(v=VS.100).aspx
I don't particularly like it very much, the tables that it generates aren't normalized, but well, it's true that we shouldn't be using huge and complex datastores for WP7 apps, it's more to store some basic info that's too much to handle by isolated storage on itself..
Following this approach, it worked. I have now a collection of the base class, and i can put into it any of it's derived children. Then to retrieve them back, I use the discriminator in a linq to sql query to get one derived class or another.
Related
I'm currently working on a Windows Forms application, and I would like to use a simple database which doesn't require a backing server or credentials to run, so I chose SQLite. But trying to get this thing to work so far was a nightmare.
Right now, I have a few, simple classes with simple properties, which I'd like to store in the database. I've appended the appropriate labels to everything ([Table("")] for classes, [PrimaryKey, AutoIncrement] for the Id property), but whenever I do Connection.CreateTable(CreateFlags.AutoIncPK) (it won't show the generic parameter, but it's there, I promise), it throws a NotSupportedException, saying "Don't know about MyProject.MyClass". I have also provided an empty, parameterless constructor in each class.
So, how do I make SQLite "know" about my class?
EDIT:
My Character.cs file:
[Table("Character")]
class Character
{
[PrimaryKey, AutoIncrement]
public int id { get; set; }
public string Name
{
get;
set;
}
public string FilePath
{
get;
set;
}
public Character()
{
}
public Character(string file)
{
this.FilePath = file;
this.Name = FetchName(file);
}
private string FetchName(string file)
{
string[] fileHolder = File.ReadAllLines("\\chars\\" + file);
foreach (string line in fileHolder)
{
if (line.ToLower().StartsWith("name"))
{
if (!line.Contains(';'))
return line.Split('=')[1].Trim().Trim('"');
else return line.Split('=')[1].Split(';')[0].Trim().Trim('"');
}
}
return string.Empty;
}
}
My Database.cs file:
class Database
{
private static SQLiteConnection Connection;
private static string ConnectionString = "MyProject.sqlite";
public Database()
{
if (!File.Exists("MyProject.sqlite"))
System.Data.SQLite.SQLiteConnection.CreateFile("MyProject.sqlite");
using (Connection = new SQLiteConnection(ConnectionString))
{
Connection.CreateTable<Character>(CreateFlags.AutoIncPK);
}
}
}
The class that describe your table has to be public.
In your example, the character class is not public.
You should change the declaration like this:
[Table("Character")]
public class Character
{
// your class code...
}
I know it's a bit late to answer the original question but since my Google-Fu led me here, I hope this will help others that encounter the same error message:
I started receiving this error message after I added as public static property to a class with a [table] attribute. After adding the [Ignore] attribute to the property, I no longer received the error message.
I've inherited a MVC project that seems to use Telerik Open Access to handle data instead of using something I'm more familiar with like entity framework. I'm trying to understand the whole concept of how to work with this data method, but right now I'm just needing to find out how to add a table. I've limited my code examples to one table, but in reality there are dozens of them.
So I see that the class OpenAccessContext.cs has a database connection string, but it also has a IQueryable item made up of the class tblMaterial. The tblMaterial class is defined in tblMaterial.cs. I don't understand how this class is connected to the SQL database version of tblMaterial (so feel free to educate me on that).
I have a table called tblContacts in the SQL database. What do I need to do to connect it to my project? There's no "update from database" option when I right click any object in the solution (because they're all just classes). Will I need to create a new class manually called tblContacts.cs? If so, how do I connect it to the database version of tblContacts? Am I going to need to manually change multiple classes to add the table (OpenAccessContext, MetadataSources, Repository, etc.)?
I tried to keep this as one simple question (how do I add a table) so I don't get dinged, but any light you can shine on the Telerik Open Access would be helpful. (Please don't ding me for asking that!) I checked out the Telerik documentation here: http://docs.telerik.com/data-access/developers-guide/code-only-mapping/getting-started/fluent-mapping-getting-started-fluent-mapping-api , but it's related to setting up a new open access solution. I need to know how to modify one (without ruining the already working code). Thank you in advance for your help!
Here's the solution as seen in Visual Studio:
Open Access
Properties
References
OpenAccessContext.cs
OpenAccessMetadataSources.cs
Repository.cs
tblMaterial.cs
Here's the code:
OpenAccessContext.cs
namespace OpenAccess
{
public partial class OpenAccessContext : OpenAccessContext
{
static MetadataContainer metadataContainer = new OpenAccessMetadataSource().GetModel();
static BackendConfiguration backendConfiguration = new BackendConfiguration()
{
Backend = "mssql"
};
private static string DbConnection = ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString;
private static int entity = ConfigurationManager.AppSettings["Entity"] == "" ? 0 : int.Parse(ConfigurationManager.AppSettings["Entity"]);
public OpenAccessContext() : base(DbConnection, backendConfiguration, metadataContainer)
{
}
public IQueryable<tblMaterial> tblMaterials
{
get
{
return this.GetAll<tblMaterial>(); //.Where(a => a.EntityId == entity);
}
}
}
}
OpenAccessMetadataSources.cs
namespace OpenAccess
{
public class OpenAccessMetadataSource : FluentMetadataSource
{
protected override IList<MappingConfiguration> PrepareMapping()
{
var configurations = new List<MappingConfiguration>();
// tblMaterial
var materialConfiguration = new MappingConfiguration<tblMaterial>();
materialConfiguration.MapType(x => new
{
MaterialId = x.MaterialId,
MaterialName = x.MaterialName,
MaterialDescription = x.MaterialDescription,
MaterialActive = x.MaterialActive,
MaterialUsageType = x.MaterialUsageType,
AddDate = x.AddDate,
AddBy = x.AddBy,
ModDate = x.ModDate,
ModBy = x.ModBy
}).ToTable("tblMaterial");
materialConfiguration.HasProperty(x => x.MaterialId).IsIdentity(KeyGenerator.Autoinc);
}
}
}
Repository.cs
namespace OpenAccess
{
public class Repository : IRepository
{
#region private variables
private static OpenAccessContext dat = null;
#endregion private varibles
#region public constructor
/// <summary>
/// Constructor
/// </summary>
public Repository()
{
if (dat == null)
{
dat = new OpenAccessContext();
}
}
#endregion public constructor
#region Material (tblMaterials)
public int CreateMaterial(tblMaterial itm)
{
try
{
dat.Add(itm);
dat.SaveChanges();
return itm.MaterialId;
}
catch (Exception)
{
return 0;
}
}
}
}
tblMaterial.cs
namespace OpenAccess
{
public class tblMaterial
{
public int MaterialId { get; set; }
public string MaterialName { get; set; }
public string MaterialDescription { get; set; }
public bool MaterialActive { get; set; }
public int MaterialUsageType { get; set; }
public DateTime? AddDate { get; set; }
public string AddBy { get; set; }
public DateTime? ModDate { get; set; }
public string ModBy { get; set; }
}
}
In the case of tblContacts, I would suggest to you the following workflow for extending the model:
Add a new class file that will hold the definition of the tblContact POCO class. In this class add properties that will correspond to the columns of the table. The types of the properties should logically match the datatypes of the table columns.
In the OpenAccessMetadataSource class, add a new MappingConfiguration<tblContact> for the tblContact class and using explicit mapping provide the mapping details that logically connect the tblContact class with the tblContacts table. Make sure to add both the existing and the new mapping configurations to the configurations list.
Expose the newly added class through an IQueryable<tblContact> property in the context. This property will allow you to compose LINQ queries against the tblContacts table.
Regarding the Repository class, it seems like it is related to the custom logic of the application. It surely is not a file generated by Data Access. Therefore, you need to discuss it in your team.
I also strongly advise you against using OpenAccess in the namespaces of your application. This is known to interfere with the Data Access' namespaces during build time and at some point it causes runtime errors.
I hope this helps.
In my app, i have a edmx file with some partial classes representing my tables, and a context class wich i have the methods i need i.e GetMessages() and GetMessageById(long idMessage).
In that case, i use the GetMessages() method to fill a grid. Everything normal.
The Message entity class is something like this:
[Table("Message")]
public partial class Message
{
public long IdMessage{get;set;}
public long IdStatus{get;set;}
}
The problem is that i have another table that i have the StatusDescription that i need to get using the IdStatus.
I created another partial class with this property:
public partial class Message
{
private static readonly MessageRepository MessageRepository ;
static Message()
{
MessageRepository = new MessageRepository();
}
public string StatusDescription
{
get { return MessageRepository .GetMessageDescription(this.Cd_SchedulerStatus); }
}
}
And the method in the MessageRepository:
public MessageRepository()
{
_appContext= AppContext.GetContext();
}
public string GetMessageStatusDescription(int statusId)
{
var status = _appContext.Message.FirstOrDefault(id => id.IdStatus.Equals(statusId));
return status != null ? status.StatusDescription : string.Empty;
}
I know that it generates problems and it is not the best approach to deal with it, because im acessing the data inside the entity class, im having the n+1 problem, each time i send a new query to the database.
I would like to know if somebody have this problem and whats the best solution?
I suggest you create a new context for each message description request:
public string GetMessageStatusDescription(int statusId)
{
using (var appContext = AppContext.GetContext())
{
var status = appContext.Message.FirstOrDefault(id => id.IdStatus == statusId);
return status != null ? status.StatusDescription : string.Empty;
}
}
So I'm building my first larger application and I'm using WPF for Windows and stuff and Entity Framework for retrieving, updating and storing data.So far using a pattern similar to the MVVM pattern, I had a couple of issues but was able to resolve them and am quite far into design.
Also, I'm using database first approach.
But I have just ran into a brick wall that I should have anticipated. It has to do with nested properties in entities and the way changes to them are handled. Let's explain.
For the purpose of simplicity I will not be using my actual class names.
So let's say I have three entities in my EF Model: Department, Manager and PersonalInfo.
I modified my *.tt Template file so that all my entities also implement INotifyPropertyChanged interface, but only for their NON NAVIGATION properties since Navigation properties are declared as virtual and WILL be overridden by EF when their date gets set.
So let's say my generated classes look like this:
public partial class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Department() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
int _someproperty;
public int SomeProperty { get { return _someproperty; } set { _someproperty= value; OnPropChange("SomeProperty"); } }
int _managerid;
public int ManagerID { get { return _managerid; } set { _managerid = value; OnPropChange("ManagerID"); } }
public virtual Manager Manager { get; set; }
}
public partial class Manager : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Manager() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
public virtual PersonalInfo PersonalInfo { get; set; }
}
public partial class PersonalInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public PersonalInfo() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
string _firstname;
public string FirstName { get { return _firstname; } set { _firstname = value; OnPropChange("FirstName"); } }
string _lastname;
public string LastName { get { return _lastname; } set { _lastname = value; OnPropChange("LastName"); } }
}
Now this works pretty well if I want to let's say display a list of Departments with their Managers. First I load the data into the EF Context like so
Context.Departments.Include(d => d.Manager.PersonalInfo).Load();
Departments = Context.Deparments.Local;
And than in the XAML I can do:
<DataGrid ItemsSource="{Binding Departments}" SelectedItem="{Binding CurrentDepartment, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}"/>SomeProperty
<DataGridTextColumn Binding="{Binding SomeProperty }" Header="Property"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.FirstName}" Header="FirstName"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.LastName}" Header="LastNameName"/>
</DataGrid.Columns>
</DataGrid>
And all of this works wonderfully. I can add and remove items with no problems by simply removing them from Context and saving changes. Since entity sets are ObservableCollections any additions or removal from them automatically raises appropriate events which update the datagrid. I can also modify any nonnavigation property of the Department and can refresh the data in CurrentDepartment like so:
Context.Entry(CurrentDepartment).Refresh();
and it automatically refreshes the data in the datagrid.
Problems start when I change one of the navigation properties. Let's say that I opened a window in which I edited the Department, where I changed the current manager from Bob Bobington to Dave Daveston. When I return to this window calling:
Context.Entry(CurrentDepartment).Refresh();
It will only refresh non navigation properties, and First and Lastname columns will still say Bob Bobington. But that is Refresh function working as intended. But if I load the correct data into the context like this:
Context.Entry(CurrentDepartment).Reference(d=>d.Manager);
Context.Entry(CurrentDepartment.Manager).Reference(m=>m.PersonalInfo);
is still won't change the contents of the first and last name columns since they are still bound to the OLD manager. They will only refresh if the change happens on Bob Bobington instance of PersonalInfo.
I can sort of solve this level of problem by binding the column directly to Manager property, and converting Manager to text either via a ValueConverter or by overriding ToString for Manager. But that won't help since WPF won't ever be notified that Manager property has changed since changes to that property don't raise PropertyChanged event.
Navigation properties can not raise that event since even if I edited the tt template so it generates the code for the navigation property like so:
Manager _manager;
public virtual Manager Manager { get{return _manager;}
set{
_manager=value;
OnPropChange("Manager");
}
}
all this code will likely be overridden by the EF framework itself.
Sooo, what is the best thing to do in these cases? Please don't tell me that conventional wisdom is to copy the data from EF Poco classes into your own and use them. :(
UPDATE:
Here goes a potentially stupid solution for this problem. But it works.
ObservableCollection<Department> tempd = Departments;
Department temp = CurrentDepartment;
Departments = null;
CurrentDepartment = null;
Context.Entry(temp).Refresh();
Context.Entry(temp).Reference(d=>d.Manager).Load();
Context.Entry(temp.Manager).Reference(m=>m.PersonalInfo).Load();
Departments = tempd;
CurrentDepartment = temp;
As you can clearly see the key is in forcing the DataGrid to rebind itself from scratch. This way it will use no shortcuts and will rebind itself properly. BUT this method is quite silly. I shiver at the thought of having to do this to datagrids with hundreds of rows.
So I'm still waiting for a proper solution, but I'll be continuing onwards using this. Something is better than nothing.
Well, conventional wisdom is to copy the data across to another POCO, or at least make your ViewModel class peek through to an underlying Model class. You have combined your Model and ViewModel classes such that Model-based constraints (virtual methods required by your ORM) are interfering with your ViewModel-based constraints (to allow databinding, you must be able to raise events from property setters).
If your Model and ViewModel were properly separated (Separation of Concerns) then you could have your virtual methods and database-required fields on your Model (a DB persistable object) and your purely View-based functions (PropertyChanged events) on your ViewModel. Your DB code should never care about your PropertyChanged events anyway.
You can make it easier by making the ViewModel a look-through class so every property getter-setter looks like:
public string PropertyThing
{
get { return _myModel.PropertyThing; }
set { _myModel.PropertyThing = value; PropChanged("PropertyThing"); }
}
If you're already doing code generation this shouldn't be a major chore.
Alternatively, you could duplicate all the values with something like AutoMapper to separate out your Model and ViewModel to separate classes.
It's not what you wanted to hear, but your ORM and your UI are clashing and that's the sort of thing that MVVM architecture (specifically separating the Model and ViewModel) are supposed to make better.
I am trying to create a simple setting in Orchard that appears in the settings page. I have created a module which is adding my ContentPart to the settings page and is correctly creating a table in the database but every time the cshtml file is rendered and the property of the record is accessed I keep getting the following NHibernate Record.
No persister for: TekFlow.Contact.TekFlowEmailSettingsPartRecord.
(TekFlow.Contact is the Module name)
Below is all of the code that I am using to create the Record/Part/Handler/Driver needed in Orchard.
public class TekFlowEmailSettingsPartDriver : ContentPartDriver<TekFlowEmailSettingsPart>
{
public TekFlowEmailSettingsPartDriver()
{
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
protected override DriverResult Editor(TekFlowEmailSettingsPart part, dynamic shapeHelper)
{
return ContentShape("Parts_TekFlowEmailSettings_Edit",
() => shapeHelper.EditorTemplate(TemplateName: "Parts.TekFlowEmailSettings", Model: part, Prefix: Prefix)
);
}
protected override DriverResult Editor(TekFlowEmailSettingsPart part, Orchard.ContentManagement.IUpdateModel updater, dynamic shapeHelper)
{
bool success = updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
[UsedImplicitly]
public class TekFlowEmailSettingsPartHandler : ContentHandler
{
public TekFlowEmailSettingsPartHandler(IRepository<TekFlowEmailSettingsPartRecord> repository)
{
Filters.Add(new ActivatingFilter<TekFlowEmailSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
}
}
public class TekFlowEmailSettingsPartRecord : ContentPartRecord {
public virtual string SendToEmail { get; set; }
}
public class TekFlowEmailSettingsPart : ContentPart<TekFlowEmailSettingsPartRecord>
{
public string SendToEmail
{
get { return Record.SendToEmail; }
set { Record.SendToEmail = value; }
}
}
public class TekFlowEmailSettingsDataMigration : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("TekFlowEmailSettingsPartRecord",
table => table
.ContentPartRecord()
.Column<string>("SendToEmail", c => c.WithDefault("SomeEmail#somedomain.com").WithLength(255))
);
ContentDefinitionManager.AlterPartDefinition(
typeof(TekFlowEmailSettingsPart).Name, cfg => cfg.Attachable());
return 1;
}
}
Turns out that if your Part and Record are not in your "Models" namespace that this wont work in orchard. When I changed the Namespace for the two classes it worked. Must be an assumption that Orchard is making.
I got the same error from not having virtual variables in my record.
(In my case it did not inherit ContentPartRecord and declared it's own Id, not sure if the issue simply was that Id was not virtual or that all variables had to be virtual.)
Also as mentioned above your namespace must end with Models or Records, as explained here:
https://orchard.codeplex.com/discussions/267968
My Favicon module has pretty much the same structure and when I did a file by file comparison I could not find a significant difference. The only thing that looks suspicious is that you didn't define a prefix in your driver. That may interfere with the binder's ability to rehydrate the model but I'm not sure how that would affect persistence.