I have a small project using PetaPoco to re-implement Membership Provider, Role Provider and Profile Provider of ASP.NET. And each table has two common field: name and lowered_name. And I face an issue when try to insert new record into database using Database.Insert(), the lowered_name field can not be insert.
E.g we have a table called Category
CREATE TABLE `category` (
`name` varchar(100) NOT NULL,
`lowered_name` varchar(100) NOT NULL,
PRIMARY KEY (`lowered_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And a Category entity:
[TableName("category"), PrimaryKey("lowered_name")]
public class Category
{
[Column("name")]
public string Name { get; set; }
[Column("lowered_name")]
public string LoweredName { get; set; }
}
I have use PetaPoco to insert a new category like below
var name = Guid.NewGuid().ToString();
var cat = new Category() { Name = name, LoweredName = name.ToLower() };
var db = new Database("Test");
db.Insert(cat);
var retrievecat = db.SingleOrDefault<Category>("where lowered_name like #0", name.ToLower());
Console.WriteLine(retrievecat.LoweredName);
Assert.IsTrue(retrievecat != null);
However, this code doesn't work, a new record was create but the lowered_name field of the record is null.
When I change the db.Insert(cat) to:
db.Execute("insert into category(name, lowered_name) values (#0, #1)", name, name.ToLower());
Everything is fine.
I finished implement a lot of code for these providers above and they work well with PostGreSQL and SQL Server, and I don't want to re-implement every Insert function of each repository (I create a common Insert function in the AbstractRepository class).
Do you have any idea?
SchoTime has helped me to fixed the problem if lowered_name is a primary key. However, in my real app, name and lowered_name are not primary key.
I use an abstract class:
public abstract class AbstractEntity
{
#region Fields
private string _name;
#endregion Fields
#region Properties
[PetaPoco.Column(Name = "id")]
public long Id { get; set; }
[Required]
[PetaPoco.Column(Name = "name")]
public string Name
{
get { return _name; }
set
{
_name = value;
LoweredName = value.ToLower();
}
}
[Required]
[PetaPoco.Column(Name = "lowered_name")]
public string LoweredName { get; set; }
[PetaPoco.Column(Name = "description")]
public string Description { get; set; }
#endregion Properties
#region Initialization
public AbstractEntity()
{
}
public AbstractEntity(string name)
{
Name = name;
}
#endregion Initialization
}
And the User entity:
[PetaPoco.TableName("vietspring_security_user"), PetaPoco.PrimaryKey("id")]
public class User:AbstractEntity
{
#region Private Variables
private string _email;
#endregion
#region Constructors
public User():base()
{
InitMembers();
}
public User(int id)
{
Id = id;
InitMembers();
}
public User(string name): base(name)
{
Name = name;
InitMembers();
}
#endregion
#region Properties
[PetaPoco.Column("password")]
public virtual string Password { get; set; }
[PetaPoco.Column("password_format")]
public virtual int PasswordFormat { get; set; }
[PetaPoco.Column("password_salt")]
public virtual string PasswordSalt { get; set; }
[PetaPoco.Column("email")]
public virtual string Email
{
get
{
return _email;
}
set
{
_email = value;
LoweredEmail = value.ToLower();
}
}
[PetaPoco.Column("lowered_email")]
public virtual string LoweredEmail { get; set; }
[PetaPoco.Column("password_question")]
public virtual string PasswordQuestion { get; set; }
[PetaPoco.Column("password_answer")]
public virtual string PasswordAnswer { get; set; }
[PetaPoco.Column("comments")]
public virtual string Comments { get; set; }
[PetaPoco.Column("is_approved")]
public virtual bool IsApproved { get; set; }
[PetaPoco.Column("is_locked_out")]
public virtual bool IsLockedOut { get; set; }
[PetaPoco.Column("creation_date")]
public virtual DateTime CreationDate { get; set; }
[PetaPoco.Column("last_activity_date")]
public virtual DateTime LastActivityDate { get; set; }
[PetaPoco.Column("last_login_date")]
public virtual DateTime LastLoginDate { get; set; }
[PetaPoco.Column("last_locked_out_date")]
public virtual DateTime LastLockedOutDate { get; set; }
[PetaPoco.Column("last_password_change_date")]
public virtual DateTime LastPasswordChangeDate { get; set; }
[PetaPoco.Column("failed_password_attempt_count")]
public virtual int FailedPasswordAttemptCount { get; set; }
[PetaPoco.Column("failed_password_attempt_window_start")]
public virtual DateTime FailedPasswordAttemptWindowStart { get; set; }
[PetaPoco.Column("failed_password_answer_attempt_count")]
public virtual int FailedPasswordAnswerAttemptCount { get; set; }
[PetaPoco.Column("failed_password_answer_attempt_window_start")]
public virtual DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
[PetaPoco.ResultColumn]
public virtual IList<Application> Applications { get; set; }
#endregion
...
}
And in MembershipProvider, I call these below methods to create new User:
var userId = Convert.ToInt64(_userRepository.Insert(user));
_userRepository.AddApplication(app.Id, userId);
status = MembershipCreateStatus.Success;
I'm success in save user but name and lowered_name fields are null. How about this situation?
Because you have a primary key of lowered_name and it doesn't appear to be an auto increment column then you must set autoIncrement false as below.
[PrimaryKey("lowered_name", autoIncrement=false)]
AutoIncrement is true by default.
Related
I have 2 questions:
Why am I getting that exception? I want to create a row in my User Table (it has 6 columns: Name, Role, Email, Group, Id, SoftwareVersion) and in the Membership table. The primary key is the pair Id-SoftwareVersion. The code:
code
What's wrong?
Is it possible add more row (with CreateUserAndAccount) by changing the id? Right? Example: try to add a row with "Administrator" as UserName when an entry already exists in the DB with the same name (but I remember the primary key is the pair Id-SoftwareVersion).
My User class:
public partial class User
{
public User()
{
this.dfcs = new HashSet<Dfc>();
this.fids = new HashSet<Fid>();
this.histroyDeliverRequests = new HashSet<Histroyrequest>();
this.histroyProposalRequests1 = new HashSet<Histroyrequest>();
}
public string Name { get; set; }
public string Role { get; set; }
public string Email { get; set; }
public string Group { get; set; }
public int Id { get; set; }
public short SoftwareVersion { get; set; }
public virtual ICollection<Dfc> dfcs { get; set; }
public virtual ICollection<Fid> fids { get; set; }
public virtual ICollection<Histroyrequest> histroyDeliverRequests { get; set; }
public virtual ICollection<Histroyrequest> histroyProposalRequests1 { get; set; }
public virtual Software Software { get; set; }
}
I am thinking you should omit the Id, when inserting into the User Table, as I would think that the Id would auto increment, when inserting a new record?
Below are two tables that has a many to many relation and and also another table that has a relation with the two first. A mapping table is created by Visual Studio with the name OrderEmployee, but when I run the application and enter some information to the Timeunit create form, the mapping table OrderEmployee is empty!
Should there not be some IDs added to that table since it has a relation with the Timeunit table or how is this mapping table thing working, when will there be data added to the mapping table? Or is something wrong with my Entity Classes?
public class Order
{
public int ID { get; set; }
public string Name { get; set; }
public int? ManufacturerID { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public int EmployeeNumber { get; set; }
public virtual ICollection<Timeunit> Timeunits { get; set; }
public virtual ICollection<Order> Order { get; set; }
}
public class Timeunit
{
public int ID { get; set; }
public int Week { get; set; }
public int HoursPerWeek { get; set; }
public int EmployeeID { get; set; }
public int? OrderID { get; set; }
public virtual Employee Employee { get; set; }
public virtual Order Order { get; set; }
}
EDIT:
Create Method for Timeunit:
public ActionResult Create([Bind(Include = "ID,Week,HoursPerWeek,EmployeeID,OrderID")] Timeunit timeunit)
{
if (ModelState.IsValid)
{
db.Timeunits.Add(timeunit);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ProjectID = new SelectList(db.Orders, "ID", "Name", timeunit.OrderID);
ViewBag.ResourceID = new SelectList(db.Employees, "ID", "Name", timeunit.EmployeeID);
return View(timeunit);
}
I am writing a PhoneGap/Web/JS mobile application that uses the WebAPI and Entity Framework in the backend.
I have a class called Thing which references the User table 4 times (ChangedByUserId, CreatedByUserId etc. The User table is really large (30 user-related fields)
I want to pass as little data over each call as possible, but I need the User's Name for each of these UserID foreign keys. (this is the only information from the user record I need).
When I use the object graph in EF it returns the FULL user record for each foreign key, so a single Thing object becomes massively bloated. I don't want to make multiple calls to get the Thing POCO object and then the User's name by UserID.
What I really want to do is a sort of flattened DTO object which is just the Thing class below but with a string for each user name, e.g. CreatedByUserName, ChangedByUserName etc. Then I would return this DTO as my hydrated POCO object and the data would be small.
So my question is: How do I do this using Entity Framework? (limit related record's return data?)
public partial class Thing
{
public int ThingId { get; set; }
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public string ThingText { get; set; }
public int StatusId { get; set; }
public int ChangedByUserId { get; set; }
public int CreatedByUserId { get; set; }
public virtual User FromUser { get; set; }
public virtual User ToUser { get; set; }
public virtual User CreatedByUser { get; set; }
public virtual User ChangedByUser { get; set; }
}
As you said, you need to flatten Thing
public class FlatThing
{
public int ThingId { get; set; }
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public string ThingText { get; set; }
public int StatusId { get; set; }
public int ChangedByUserId { get; set; }
public int CreatedByUserId { get; set; }
public string FromUserName { get; set; }
public string ToUserName{ get; set; }
}
// assume you have your things
var flatThings = new List<FlatThings>;
foreach (Thing t in things)
flatThings.Add(new FlatThing{ ThingId = t.ThingId, FromUserId = t.FromUserId,
FromUserName = t.FromUser.Name .....});
return flatThings;
I want to created Bi-Directional One-One relationship between two entities using EF Code First. I have trouble with the following code. What do you think I should do?
public class User
{
public string ID { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public int ProfileID { get; set; }
public Profile Profile { get; set; }
}
public class Profile
{
public int UserID { get; set; }
public User User { get; set; }
public int ProfileID { get; set; }
public string ProfileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
}
I want to have both Navigation property and Foreign Key in both the entities.
This gives me error. What can do I in Fluent Mapping API to make this work?
Use this:
public class User
{
public string ID { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public Profile Profile { get; set; }
}
public class Profile
{
[Key, ForeignKey("User")]
public int ProfileID { get; set; }
public string ProfileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
public User User { get; set; }
}
That is the only valid way to build one-to-one relation in EF - PK of the dependent entity must be also FK to principal entity. There is nothing like bidirectional one-to-one relation in EF because it cannot work in EF.
The way how people sometimes overcome this are two one-to-many relations where principal doesn't have navigation collection for dependent entities + manually defined unique keys in the database. That require manual mapping:
public class User
{
public string ID { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
// one side MUST be nullable otherwise you have bidirectional constraint where each
// entity demands other side to be inserted first = not possible
public int? ProfileId { get; set; }
public Profile Profile { get; set; }
}
public class Profile
{
public int ProfileID { get; set; }
public string ProfileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
And in mapping you will define:
modelBuilder.Entity<User>
.HasOptional(u => u.Profile)
.WithMany()
.HasForeignKey(u => u.ProfileId);
modelBuilder.Entity<Profile>
.HasRequired(u => u.User)
.WithMany()
.HasForeignKey(u => u.UserId);
Now you must define Unique keys in the database - if you are using code first use custom database initializer. Be aware that still bidirectional one-to-one is wrong concept because both sides demand unique FK where NULL is still included in unique values so once you insert User before Profile there mustn't be any other User without Profile. That probably leads to serializable transaction.
I have tables like
Users (UserId,Username,Password,CreatedOn,CreatedBy)
Roles (RoleId,RoleName,Description,CreatedOn,CreatedBy)
UserRoleMap(UserRoleMapId,UserId,RoleId,CreatedOn,CreatedBy)
These are my entities:
[ActiveRecord(Table="Users")]
public class User:ActiveRecordBase<User>
{
[PrimaryKey(Generator = PrimaryKeyType.Identity, Column = "RoleId")]
public virtual int UserId { get; set; }
[Property(Column="Username")]
public virtual string Username { get; set; }
[Property(Column = "Password")]
public virtual string Password { get; set; }
[Property(Column="CreatedBy")]
public virtual string CreatedBy { get; set; }
[Property(Column="CreatedOn")]
public virtual DateTime CreatedOn { get; set; }
[HasAndBelongsToMany(Table="UserRoleMap",ColumnKey="UserId")]
public IList<Role> Roles { get; set; }
}
[ActiveRecord(Table = "Roles")]
public class Role : ActiveRecordBase<User>
{
[PrimaryKey(Generator = PrimaryKeyType.Identity, Column = "RoleId")]
public virtual int RoleId { get; set; }
[Property(Column = "RoleName")]
public virtual string RoleName { get; set; }
[Property(Column = "Description")]
public virtual string Description { get; set; }
[Property(Column = "CreatedBy")]
public virtual string CreatedBy { get; set; }
[Property(Column = "CreatedOn")]
public virtual DateTime CreatedOn { get; set; }
[HasAndBelongsToMany(Table = "UserRoleMap", ColumnKey = "RoleId")]
public IList<User> Users { get; set; }
}
[ActiveRecord(Table="UserRoleMap")]
public class UserRoleMap:ActiveRecordBase<UserRoleMap>
{
[PrimaryKey(Generator = PrimaryKeyType.Identity, Column = "UserRoleMapId")]
public virtual int UserRoleMapId { get; set; }
[BelongsTo(Column="UserId",Table="Users")]
public virtual User UserId { get; set; }
[BelongsTo(Column = "RoleId", Table = "Roles")]
public virtual Role RoleId { get; set; }
}
I keep getting this error:
ActiveRecordSample.Tests.FrameworkInitializationTest.CanInitializaFramework : Castle.ActiveRecord.Framework.ActiveRecordException : Property UserId references table "Users", which does not have a corresponding [JoinedTable] on the class.
Wherever the column name matches the property name, you don't need to set Column="..."
User.UserId is mapped to column "RoleId", it should be "UserId" (or as I said in the above point, just don't define it)
Make sure to understand the pros and cons of each PK generator.
When using HasAndBelongsToMany you don't want a separate relationship class (in your case UserRoleMap).
IIRC you also need to define the other FK in HasAndBelongsToMany with ColumnRef.