EF7 without setters - entity-framework-core

I'm migrating a project from EF core 2 to EF 7 when i found that in EF 7 after scaffolding, context models no longer have setters on virtual collection, example:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
when inserting a new blog, I used to do it this way:
var blog = new Blog()
{
Name = "",
Author = "",
Posts = new List<Post>() {
new Post(){Title="", Content = "" },
new Post(){Title="", Contect = "" },
new Post(){Title="", Contect = "" },
}
};
_context.RetailLot.Add(blog);
_context.SaveChanges();
Apparently I cant do this anymore since it doesn't have a setter and my 2 options would be to add init keyword replacing the setter or keep it without a setter but manually add the posts by using someething like this
var blog = new Blog()
{
Name = "",
Author = ""
}
blog.Posts.Add(new Post(){Title="", Content = "" });
blog.Posts.Add(new Post(){Title="", Content = "" });
blog.Posts.Add(new Post(){Title="", Content = "" });
Are these indeed the only ways cause the second seems very manual and with adding init than force-scaffolding would replace it without using T4 templates...
Also I came across many instances where we fetch from context and we customize the select statement like below
_context.Blogs.include(x=>x.Posts).Select( b => new Blog(){
Name = "MyBlog_"+x.Name,
Author = x.Author,
Posts = x.Posts
});
this is a simple example to how I needed to manipulate the returned object by selecting some specific columns to be returned or customize some columns. Posts=x.Posts will error out here because its a readonly. is adding init keyword my only acceptable option ? other workarounds is to create a new model that extend Blog but also override it to include a setter..

Related

Internationalization of content in Entity Framework

I keep coming across an i18n requirement where my data (not my UI) needs to be internationalized.
public class FooEntity
{
public long Id { get; set; }
public string Code { get; set; } // Some values might not need i18n
public string Name { get; set } // but e.g. this needs internationalized
public string Description { get; set; } // and this too
}
What are some approaches I could use?
Some things I've tried:-
1) Store a resource key in the db
public class FooEntity
{
...
public string NameKey { get; set; }
public string DescriptionKey { get; set; }
}
Pros: No need for complicated queries to get a translated entity. System.Globalization handles fallbacks for you.
Cons: Translations can't easily be managed by an admin user (have to deploy resource files whenever my Foos change).
2) Use a LocalizableString entity type
public class FooEntity
{
...
public int NameId { get; set; }
public virtual LocalizableString Name { get; set; }
public int NameId { get; set; }
public virtual LocalizableString Description { get; set; }
}
public class LocalizableString
{
public int Id { get; set; }
public ICollection<LocalizedString> LocalizedStrings { get; set; }
}
public class LocalizedString
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual LocalizableString Parent { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Value { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string CultureCode { get; set; }
}
Pros: All localised strings in the same table. Validation can be performed per-string.
Cons: Queries are horrid. Have to .Include the LocalizedStrings table once for each localizable string on the parent entity. Fallbacks are hard and involve extensive joining. Haven't found a way to avoid N+1 when retrieving e.g. data for a table.
3) Use a parent entity with all the invariant properties and child entities containing all the localized properties
public class FooEntity
{
...
public ICollection<FooTranslation> Translations { get; set; }
}
public class FooTranslation
{
public long Id { get; set; }
public int ParentId { get; set; }
public virtual FooEntity Parent { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set }
public string Description { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string CultureCode { get; set; }
}
Pros: Not as hard (but still too hard!) to get a full translation of an entity into memory.
Cons: Double the number of entities. Can't handle partial translations of an entity - especially the case where, say, Name is coming from es but Description is coming from es-AR.
I have three requirements for a solution
Users can edit entities, languages, and translations at runtime
Users can supply partial translations with missing strings coming from a fallback as per System.Globalization
Entities can be brought into memory without running into e.g. N+1 issues
Why don't you take the best of both worlds?
Have a CustomResourceManager that handles the loading of resources and picking the right culture and use a CustomResourceReader that uses whatever backing store you like. A basic implementation could look like this, relying on convention of the Resourceky being Typename_PropertyName_PropertyValue. If for some reason the structure of the backingstore(csv/excel/mssql/table structure) need to change you only have the change the implementation of the ResourceReader.
As an added bonus I also got the real/transparent proxy going.
ResourceManager
class MyRM:ResourceManager
{
readonly Dictionary<CultureInfo, ResourceSet> sets = new Dictionary<CultureInfo, ResourceSet>();
public void UnCache(CultureInfo ci)
{
sets.Remove(ci):
}
protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
ResourceSet set;
if (!sets.TryGetValue(culture, out set))
{
IResourceReader rdr = new MyRR(culture);
set = new ResourceSet(rdr);
sets.Add(culture,set);
}
return set;
}
// sets Localized values on properties
public T GetEntity<T>(T obj)
{
var entityType = typeof(T);
foreach (var prop in entityType.GetProperties(
BindingFlags.Instance
| BindingFlags.Public)
.Where(p => p.PropertyType == typeof(string)
&& p.CanWrite
&& p.CanRead))
{
// FooEntity_Name_(content of Name field)
var key = String.Format("{0}_{1}_{2}",
entityType.Name,
prop.Name,
prop.GetValue(obj,null));
var val = GetString(key);
// only set if a value was found
if (!String.IsNullOrEmpty(val))
{
prop.SetValue(obj, val, null);
}
}
return obj;
}
}
ResourceReader
class MyRR:IResourceReader
{
private readonly Dictionary<string, string> _dict;
public MyRR(CultureInfo ci)
{
_dict = new Dictionary<string, string>();
// get from some storage (here a hardcoded Dictionary)
// You have to be able to deliver a IDictionaryEnumerator
switch (ci.Name)
{
case "nl-NL":
_dict.Add("FooEntity_Name_Dutch", "nederlands");
_dict.Add("FooEntity_Name_German", "duits");
break;
case "en-US":
_dict.Add("FooEntity_Name_Dutch", "The Netherlands");
break;
case "en":
_dict.Add("FooEntity_Name_Dutch", "undutchables");
_dict.Add("FooEntity_Name_German", "german");
break;
case "": // invariant
_dict.Add("FooEntity_Name_Dutch", "dutch");
_dict.Add("FooEntity_Name_German", "german?");
break;
default:
Trace.WriteLine(ci.Name+" has no resources");
break;
}
}
public System.Collections.IDictionaryEnumerator GetEnumerator()
{
return _dict.GetEnumerator();
}
// left out not implemented interface members
}
Usage
var rm = new MyRM();
var f = new FooEntity();
f.Name = "Dutch";
var fl = rm.GetEntity(f);
Console.WriteLine(f.Name);
Thread.CurrentThread.CurrentUICulture = new CultureInfo("nl-NL");
f.Name = "Dutch";
var dl = rm.GetEntity(f);
Console.WriteLine(f.Name);
RealProxy
public class Localizer<T>: RealProxy
{
MyRM rm = new MyRM();
private T obj;
public Localizer(T o)
: base(typeof(T))
{
obj = o;
}
public override IMessage Invoke(IMessage msg)
{
var meth = msg.Properties["__MethodName"].ToString();
var bf = BindingFlags.Public | BindingFlags.Instance ;
if (meth.StartsWith("set_"))
{
meth = meth.Substring(4);
bf |= BindingFlags.SetProperty;
}
if (meth.StartsWith("get_"))
{
// get the value...
meth = meth.Substring(4);
var key = String.Format("{0}_{1}_{2}",
typeof (T).Name,
meth,
typeof (T).GetProperty(meth, BindingFlags.Public | BindingFlags.Instance
|BindingFlags.GetProperty).
GetValue(obj, null));
// but use it for a localized lookup (rm is the ResourceManager)
var val = rm.GetString(key);
// return the localized value
return new ReturnMessage(val, null, 0, null, null);
}
var args = new object[0];
if (msg.Properties["__Args"] != null)
{
args = (object[]) msg.Properties["__Args"];
}
var res = typeof (T).InvokeMember(meth,
bf
, null, obj, args);
return new ReturnMessage(res, null, 0, null, null);
}
}
Real/Transparent proxy usage
var f = new FooEntity();
f.Name = "Dutch";
var l = new Localizer<FooEntity>(f);
var fp = (FooEntity) l.GetTransparentProxy();
fp.Name = "Dutch"; // notice you can use the proxy as is,
// it updates the actual FooEntity
var localizedValue = fp.Name;
First one is worthy if you have static content in database. For example if you have categories that relatively are not going to be changed by user. You can change them at next deploy. I do not like this solution personally. I do not consider this as a nice solution. This is just an escape of the problem.
Second one is the best but can cause a problem when you have two or more localizable fields in one entity. You can simplify it a bit and hard code languages on it like this
public class LocalizedString
{
public int Id { get; set; }
public string EnglishText { get; set; }
public string ItalianText { get; set; }
public string ArmenianText { get; set; }
}
Third one is not a good one neither. From this structure I can't be sure that all nodes (literals, lines, strings etc.) translated in specific culture.
Do not generalize too much. Each problem is kind of specialized and it needs specialized solution too. Too much generalization makes unjustified issues.

Adding new rows throws 'Multiplicity constraint violated'

I have two related classes like these :
public partial class WorkItem
{
public WorkItem()
{
this.ChildWorkItems = new HashSet<WorkItem>();
this.Attachments = new HashSet<Attachment>();
}
public int Id { get; set; }
public Nullable<int> ParentId { get; set; }
public string Title { get; set; }
public string SenderUserName { get; set; }
public virtual ICollection<WorkItem> ChildWorkItems { get; set; }
public virtual WorkItem ParentWorkItem { get; set; }
public virtual ICollection<Attachment> Attachments { get; set; }
}
and the realted 1-n attachments :
public partial class Attachment
{
public int Id { get; set; }
public string Filename { get; set; }
public Nullable<int> WorkItemId { get; set; }
public virtual WorkItem WorkItem { get; set; }
}
Now I want to insert some new workitems with their attachment into the database. I use the following code for insertion :
at first put all workitems (regardless of their attachments )
var workItems = new List<WorkItem>();
foreach (var username in AllUsers)
{
var workitem = new WorkItem();
//fill the simple fields
lst.Add(workitem);
Context.WorkItems.Add(workitem);
}
then set the attachments :
foreach (var fileName in MYFILES)
{
var file = new System.IO.FileInfo(fileName);
foreach (var workItem in workItems)
{
var att =
new Attachment()
{
Filename = file.Name,
};
context.Attachments.Add(att);
att.WorkItem = workItem;
}
}
But I get the following exception :
Multiplicity constraint violated. The role 'WorkItem' of the relationship 'AienCRMModel.FK_WorkItemAttachments_WorkItems' has multiplicity 1 or 0..1.
The interesting point is if I only have One workitem , everything is ok. If I have more than one WorkItem with no attachments , again everything is ok. The problem raises when having more than one WorkItem with at least one attachment.
I read lots of other post but nothing usefuk was found.
NOTE :
I use EF Code-First 4.3 with a T4 template which generate my classes from the EDMX file.
I Really appreciate your helps in advance.
EDIT
I Attached the full EDMX diagram of mentioned tables here :
EDIT 2
Complete .edmx file at here : http://www.4shared.com/file/zQUO_qk7/CrmModel141.html?
Somewhere in your code you are using the same object instance for different rows in the database like this (just an example):
var o1 = new O1Class();
foreach (var o2 in collection)
{
o2.O1s.Add(o1);
}
But is have to be:
foreach (var o2 in collection)
{
o2.O1s.Add(new O1Class());
}
I this example O1Class can have only one O2Class in their relationship but the Entity Framework keep the reference on the o1 instance so when you adding it to multiple O2Class instances the EF will think that the current O1Class has multiple O2Class instances which is incorrect.

Entity Framework: Entity to include a fixed number of navigational property : how to write this query

I am having 2 entities as described below
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string Message { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public Product Product { get; set; }
}
Now I want to write a query which includes only 10 comments for a particular product. The query i have written below includes all comments on product.
var product = db.Products.Include(a=>a.Comments).Take(10);
Please suggest is it possible in entity framework.
Edit 1
changed this query to
var product = db.Products.Include(a=>a.Comments).FirstOrDefault(c=>c.Id==1);
this one
var product = db.Products.Include(a=>a.Comments).Take(10);
I have to load 10 products with 10 comments each.
you can always reverse that and get the Comments with the product:
var comments = db.Comments.Include(c => c.Product)
.Where(c => c.Product.Id == 1)
.Take(10);
This will work if you don't mind returning an anonymous type or a poco:
var products = from x in db.Products.Take(10)
select new
{
ProductProperty1 = x.Property1,
Comments = x.Comments.Take(10),
};
var product = products.FirstOrDefault(c=>c.ProductProperty1 ==1);
I believe that following will do:
var comments = db.Comments.Where(c=>c.ProductId==1).Take(10).ToList();
var product = db.Products.FirstOrDefault(c=>c.Id==1);

Entity Framework - Adding parent is also trying to add child when I don't want it to

I have two objects (WishListItem and Product) in a one-to-many relationship. WishListItem has to have a product. Each Product can be in 0 - n WishListItems.
public class WishListItem
{
public int Id { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
// ... other properties
}
The Product has no knowledge of WishListItems. All of the Products exist. I just want to add a new WishListItem. My WishListItem model for the relationship is this:
HasRequired(p => p.Product).WithMany().HasForeignKey(p => p.ProductId);
When I try to add a new item like this:
WishListItem item = new WishListItem();
// ... sets properties
WishListItems.Add(item); // WishListItems is of type DbSet<WishListItem>
SaveChanges();
This code seems to try to also add a Product. I don't want to add a new Product (or even update it). The Product property is set to null. How do I tell Entity Framework that I only want to add the WishListItem? Do I need to Ignore the Product property (by doing Ignore(p => p.Product); in my WishListItem model) and load the Product separately whenever I load my WishListItem objects?
I have solved my issue. The problem came from another property on the Product object.
private bool _isFreeShippingInitialValue;
public bool IsFreeShipping
{
get
{
return _isFreeShippingInitialValue ||
computedValueFromAnotherChildObject;
}
set
{
_isFreeShippingInitialValue = value;
}
}
We noticed that when you first get the Product object, the IsFreeShipping.get is called (not sure why) before any child objects are loaded. For example, if _isFreeShippingInitialValue is false and computedValueFromAnotherChildObject is true, IsFreeShipping first returns false (because computedValueFromAnotherChildObject is first false since no child objects have been loaded), then true the next time you try to get IsFreeShipping. This makes EF think the value has changed.
The first item we added to WishListItems worked fine. It was the second item that broke. We believe SaveChanges() (or something prior to it) loaded the Product for the first WishListItem. The SaveChanges() was breaking on the Product of the first WishListItem when we were adding the second item.
So, in short, be careful when computing values in a Property.get using child objects because it can bite you in the butt.
This works for me without adding any new Addresses records. In this model, Person has an optional home address, but address doesn't have any knowledge of the person.
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Address HomeAddress { get; set; }
public int HomeAddress_id { get; set; }
}
public class Address
{
public int Id { get; set; }
public string PhoneNumber { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
In the DbContext override, I have the below code
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasRequired(t => t.HomeAddress).WithMany()
.HasForeignKey(t => t.HomeAddress_id);
}
I can write a unit test like this.
var addressId = 0;
using (var db = new DataContext())
{
var address = new Address { City = "test", Country = "test", PhoneNumber = "test", State = "test", Street = "test" };
db.Addresses.Add(address);
db.SaveChanges();
addressId = address.Id;
}
using (var db = new DataContext())
{
var person = new Person { Email = "test#test.com", FirstName = "Testy", LastName = "Tester", HomeAddress_id = addressId };
db.Persons.Add(person);
db.SaveChanges();
}

Update a document in RavenDB

I am using asp.net MVC2.
I have a model defined as
public class Department
{
[ScaffoldColumn(false)]
public object Id { get; set; }
[Required(ErrorMessage = "Department Name is required")]
[StringLength(25)]
[DisplayName("Department Name")]
public string Name { get; set; }
[DefaultValue(true)]
[DisplayName("Active?")]
public bool Active { get; set; }
}
How to update an existing department document through my controller?
my edit action is defined as
[HttpPost]
public ActionResult Edit(string id, Department department)
{
..
}
answer stated here tells there is a PATCH command to update a document.
But i didn't find this in IDocumentSession class in Raven's Client API
I do not want to first get the document and then update it like the way it is done in RavenDB's MVCMusicStore example
var albumModel = session.Load<Album>(id);
//Save Album
UpdateModel(albumModel, "Album");
session.SaveChanges();
You need to use code like this:
DocumentStore.DatabaseCommands.Batch(
new PatchCommandData{
Key = "users/15",
Patches = new [] {
Type = "Set",
Name = "Email",
Value = "Ayende"
}
}
);
See this thread on the discussion group for more info