Showing posts with label EntityFramework. Show all posts
Showing posts with label EntityFramework. Show all posts

Monday, February 11, 2013

What did you do this weekend?


Well it's been quite a full weekend!

During the work week:

  • Learned about WebAPI's IQueryable possibilities
  • Found out they changed the ruling on Entity Framework
    • We can now select straight into POCOs instead of only anonymous types or EF types
    • Still can't find any articles, blog posts, or announcements about this change.
  • Forced EF to allow me to do queries that span linked databases.
  • Used Knockout to consume the OData (or WebApi IQueryable)
    • complete with a user friendly search form
  • Explored PhantomJs for headless browser automation and testing
  • Updated my win forms tool for scraping data from our Stash and Crucible
  • Explored some SpecFlow

What did you do?

Friday, February 8, 2013

LinkedServerDatabase Query with CodeFirst Entity Framework

So you have a Model or Poco (or ViewModel) and you want to populate it via EF code first. However, the Entity Framework refuses to let you run a simple query against the connection when it involves a table that's linked. Here's the Poco Model of the linked db
[Table("Customer", Schema="LinkedDatabaseName].dbo")]
  public class Customer
  {
    [Key]
    [Column("Customer_Id")]
    public int CustomerId { get; set; }
    [Column("Company_Name")]
    [Display(Name="CustomerName")]
    public string CompanyName { get; set; }
    public string CustomerAlias { get; set; }
  }
Here's the Context Method that consumes it:
public IEnumerable<LandingViewModel> GetTrackingFiles()
    {
      var q = from tf in TrackedFiles
              join lfs in FileStatuses on tf.FileStatusId equals lfs.FileStatusId into lfsg
              from fs in lfsg.DefaultIfEmpty()
              join lc in Customers on tf.CustomerId equals lc.CustomerId into lcg
              from c in lcg.DefaultIfEmpty()
              select
                new
                  {
                    tf.FileGuid,
                    tf.FileName,
                    tf.FileId,
                    tf.CustomerId,
                    tf.ExpectedTrxCount,
                    tf.ActualTrxCount,
                    tf.ExpectedTrxPaidAmount,
                    tf.ActualTrxPaidAmount,
                    tf.ExpectedClaimCount,
                    tf.ActualClaimCount,
                    tf.FileRecieved,
                    tf.FileCompleted,
                    fs.FileStatusName,
                    tf.FilePath,
                    c.CompanyName,
                    c.CustomerAlias
                  };
      var sql = q.ToString();
      var fixedSql = sql.Replace("[LinkedDatabaseName]].dbo]", "[LinkedDatabaseName].[dbo]");
      Trace.WriteLine(fixedSql); // look at the result in the trace output
      
      var result=this.Database.SqlQuery<LandingViewModel>(fixedSql);

      

      return result;
    }

Monday, January 14, 2013

Clean your EDMX programmatically

So you have pesky underscores in your edmx, that appear every time you add a new property, table, view, relationship, or whatever. Here's the cleaning code based on Inflector and some help from a co-worker. `.Dump` is just a generic print out command available in linqpad, the code would work without those method calls.
void Main()
{
  var edmx= @"C:\Projects\psh\hpx\src\Entities\JobSystem\JobSystem.edmx";
  var xdoc=System.Xml.Linq.XDocument.Load(edmx);
  xdoc.Dump();
  var ns=xdoc.Root.Name.Namespace;
  
  //sort entity types?
  
  var storageModels=xdoc.Root.Element(ns+"Runtime").Element(ns+"StorageModels");
  var storeNs=storageModels.GetNamespaceOfPrefix("store");
  
  new {Root=ns,Default=xdoc.Root.GetDefaultNamespace(),DefaultLocal=storageModels.GetDefaultNamespace(), Store=storeNs}.Dump("namespaces");
  
  var schemas=storageModels.Elements();
  
  Debug.Assert(schemas.All (s => s.Name.LocalName=="Schema"));
  //ProcessSchemas(schemas); //not helpful or necessary
  var mappings = xdoc.Root.Element(ns+"Runtime").Element(ns+"Mappings").Elements();
  Debug.Assert(mappings.All (m => m.Name.LocalName=="Mapping"));
  ProcessMappings(mappings);
  var concept= xdoc.Root.Element(ns+"Runtime").Element(ns+"ConceptualModels");
  
  ProcessConceptual(concept);
  xdoc.Save(edmx);    
    
  
}

// Define other methods and classes here
void ProcessConceptual(XElement concept)
{
  var processed= new List<string>();
      var localNs=concept.Elements().First ().Name.Namespace;
    foreach(var s in concept.Elements())
    {
  
      localNs.Dump("Entity Type?");
      
      foreach(var et in s.Elements(localNs+"EntityType"))
      {
        foreach(var p in et.Elements(localNs+"Property"))
        {
          var pName=p.Attribute(XNamespace.None+"Name");  
          var existing=pName.Value;
          var proposed=existing.Pascalize();
          if(existing != proposed){
            processed.Add(et.Attribute(XNamespace.None+"Name").Value+":"+existing);
            pName.Value=proposed;
          }
        }
        
      }
      
    }
    var propRefs=concept.XPathSelectElements(".//*[local-name()='PropertyRef']");
    
    foreach(var propRef in propRefs)
    {
      var pName=propRef.Attribute(XNamespace.None+"Name");  
          var existing=pName.Value;
          var proposed=existing.Pascalize();
          if(existing != proposed){
            processed.Add(propRef.Parent.Name+":"+existing);
            pName.Value=proposed;
          }
    }
  
  processed.Dump("Conceptuals");
}
void ProcessMappings(IEnumerable<XElement> mappings)
{
  var processed=new List<string>();
  foreach(var m in mappings)
  {
    var localNs=m.Name.Namespace;
    foreach(var ecm in m.Elements(localNs+"EntityContainerMapping"))
    foreach(var esm in ecm.Elements(localNs+"EntitySetMapping"))
    foreach(var etm in esm.Elements(localNs+"EntityTypeMapping"))
    {
      var typeName=etm.Attribute(XNamespace.None+"TypeName");
      foreach(var mf in etm.Elements(localNs+"MappingFragment"))
      foreach(var sp in mf.Elements(localNs+"ScalarProperty"))
      {
        var spName=sp.Attribute(XNamespace.None+"Name");  
        var existing=spName.Value;
        var proposed=existing.Pascalize();
        if(existing != proposed){
          processed.Add(typeName+":"+existing);
          spName.Value=proposed;
        }
      }
    }
  }
  processed.Dump();
}


Inflector is just awesome available as a nuget package if you don't need it to be strongly signed.
/// <summary>
  /// Inflector NuGet package is not strong signed =(
  /// </summary>
  public static class Inflector
{
    readonly static List<Rule> _plurals;

    readonly static List<Rule> _singulars;

    readonly static List<string> _uncountables;

    static Inflector()
    {
        _plurals = new List<Rule>();
        _singulars = new List<Rule>();
        _uncountables = new List<string>();
        AddPlural("$", "s");
        AddPlural("s$", "s");
        AddPlural("(ax|test)is$", "$1es");
        AddPlural("(octop|vir|alumn|fung)us$", "$1i");
        AddPlural("(alias|status)$", "$1es");
        AddPlural("(bu)s$", "$1ses");
        AddPlural("(buffal|tomat|volcan)o$", "$1oes");
        AddPlural("([ti])um$", "$1a");
        AddPlural("sis$", "ses");
        AddPlural("(?:([^f])fe|([lr])f)$", "$1$2ves");
        AddPlural("(hive)$", "$1s");
        AddPlural("([^aeiouy]|qu)y$", "$1ies");
        AddPlural("(x|ch|ss|sh)$", "$1es");
        AddPlural("(matr|vert|ind)ix|ex$", "$1ices");
        AddPlural("([m|l])ouse$", "$1ice");
        AddPlural("^(ox)$", "$1en");
        AddPlural("(quiz)$", "$1zes");
        AddSingular("s$", string.Empty);
        AddSingular("(n)ews$", "$1ews");
        AddSingular("([ti])a$", "$1um");
        AddSingular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
        AddSingular("(^analy)ses$", "$1sis");
        AddSingular("([^f])ves$", "$1fe");
        AddSingular("(hive)s$", "$1");
        AddSingular("(tive)s$", "$1");
        AddSingular("([lr])ves$", "$1f");
        AddSingular("([^aeiouy]|qu)ies$", "$1y");
        AddSingular("(s)eries$", "$1eries");
        AddSingular("(m)ovies$", "$1ovie");
        AddSingular("(x|ch|ss|sh)es$", "$1");
        AddSingular("([m|l])ice$", "$1ouse");
        AddSingular("(bus)es$", "$1");
        AddSingular("(o)es$", "$1");
        AddSingular("(shoe)s$", "$1");
        AddSingular("(cris|ax|test)es$", "$1is");
        AddSingular("(octop|vir|alumn|fung)i$", "$1us");
        AddSingular("(alias|status)es$", "$1");
        AddSingular("^(ox)en", "$1");
        AddSingular("(vert|ind)ices$", "$1ex");
        AddSingular("(matr)ices$", "$1ix");
        AddSingular("(quiz)zes$", "$1");
        AddIrregular("person", "people");
        AddIrregular("man", "men");
        AddIrregular("child", "children");
        AddIrregular("sex", "sexes");
        AddIrregular("move", "moves");
        AddIrregular("goose", "geese");
        AddIrregular("alumna", "alumnae");
        AddUncountable("equipment");
        AddUncountable("information");
        AddUncountable("rice");
        AddUncountable("money");
        AddUncountable("species");
        AddUncountable("series");
        AddUncountable("fish");
        AddUncountable("sheep");
        AddUncountable("deer");
        AddUncountable("aircraft");
    }

    private static void AddIrregular(string singular, string plural)
    {
        var objArray = new object[5];
        objArray[0] = "(";
        objArray[1] = singular[0];
        objArray[2] = ")";
        objArray[3] = singular.Substring(1);
        objArray[4] = "$";
        AddPlural(string.Concat(objArray), string.Concat("$1", plural.Substring(1)));
        var objArray1 = new object[5];
        objArray1[0] = "(";
        objArray1[1] = plural[0];
        objArray1[2] = ")";
        objArray1[3] = plural.Substring(1);
        objArray1[4] = "$";
        AddSingular(string.Concat(objArray1), string.Concat("$1", singular.Substring(1)));
    }

    private static void AddPlural(string rule, string replacement)
    {
        _plurals.Add(new Rule(rule, replacement));
    }

    private static void AddSingular(string rule, string replacement)
    {
        _singulars.Add(new Rule(rule, replacement));
    }

    private static void AddUncountable(string word)
    {
        _uncountables.Add(word.ToLower());
    }

    private static string ApplyRules(List<Inflector.Rule> rules, string word)
    {
        string str = word;
        if (!_uncountables.Contains(word.ToLower()))
        {
            for (int i = rules.Count - 1; i >= 0; i--)
            {
                string str1 = rules[i].Apply(word);
                str = str1;
                if (str1 != null)
                {
                    break;
                }
            }
        }
        return str;
    }

    public static string Camelize(this string lowercaseAndUnderscoredWord)
    {
        return lowercaseAndUnderscoredWord.Pascalize().Uncapitalize();
    }

    public static string Capitalize(this string word)
    {
        return string.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower());
    }

    public static string Dasherize(this string underscoredWord)
    {
        return underscoredWord.Replace('\u005F', '-');
    }

    public static string Humanize(this string lowercaseAndUnderscoredWord)
    {
        return Regex.Replace(lowercaseAndUnderscoredWord, "_", " ").Capitalize();
    }

    private static string Ordanize(int number, string numberString)
    {
        int num = number % 100;
        if (num < 11 || num > 13)
        {
            int num1 = number % 10;
            switch (num1)
            {
                case 1:
                {
                    return string.Concat(numberString, "st");
                }
                case 2:
                {
                    return string.Concat(numberString, "nd");
                }
                case 3:
                {
                    return string.Concat(numberString, "rd");
                }
            }
            return string.Concat(numberString, "th");
        }
        else
        {
            return string.Concat(numberString, "th");
        }
    }

    public static string Ordinalize(this string numberString)
    {
        return Inflector.Ordanize(int.Parse(numberString), numberString);
    }

    public static string Ordinalize(this int number)
    {
        return Inflector.Ordanize(number, number.ToString());
    }

    public static string Pascalize(this string lowercaseAndUnderscoredWord)
    {
        string str = lowercaseAndUnderscoredWord;
        string str1 = "(?:^|_)(.)";
        return Regex.Replace(str, str1, (Match match) => match.Groups[1].Value.ToUpper());
    }

    public static string Pluralize(this string word)
    {
        return Inflector.ApplyRules(Inflector._plurals, word);
    }

    public static string Singularize(this string word)
    {
        return Inflector.ApplyRules(Inflector._singulars, word);
    }

    public static string Titleize(this string word)
    {
        string str = word.Underscore().Humanize();
        string str1 = "\\b([a-z])";
        return Regex.Replace(str, str1, (Match match) => match.Captures[0].Value.ToUpper());
    }

    public static string Uncapitalize(this string word)
    {
        return string.Concat(word.Substring(0, 1).ToLower(), word.Substring(1));
    }

    public static string Underscore(this string pascalCasedWord)
    {
        return Regex.Replace(Regex.Replace(Regex.Replace(pascalCasedWord, "([A-Z]+)([A-Z][a-z])", "$1_$2"), "([a-z\\d])([A-Z])", "$1_$2"), "[-\\s]", "_").ToLower();
    }

    private class Rule
    {
        private readonly Regex _regex;

        private readonly string _replacement;

        public Rule(string pattern, string replacement)
        {
            this._regex = new Regex(pattern, RegexOptions.IgnoreCase);
            this._replacement = replacement;
        }

        public string Apply(string word)
        {
            if (this._regex.IsMatch(word))
            {
                return this._regex.Replace(word, this._replacement);
            }
            else
            {
                return null;
            }
        }
    }
}

Tuesday, June 12, 2012

EF complex or bulk inserts from a select statement

Compose complex inserts from a select statement.
int Insert<T>(IQueryable query,IQueryable<T> targetSet)
{
      var oQuery=(ObjectQuery)this.QueryProvider.CreateQuery(query.Expression);
        var sql=oQuery.ToTraceString();
        var propertyPositions = GetPropertyPositions(oQuery);

        var targetSql=((ObjectQuery)targetSet).ToTraceString();
        var queryParams=oQuery.Parameters.ToArray();
        System.Diagnostics.Debug.Assert(targetSql.StartsWith("SELECT"));
        var queryProperties=query.ElementType.GetProperties();
        var selectParams=sql.Substring(0,sql.IndexOf("FROM "));
        var selectAliases=Regex.Matches(selectParams,@"\sAS \[([a-zA-Z0-9_]+)\]").Cast<Match>().Select(m=>m.Groups[1].Value).ToArray();
        
        var from=targetSql.Substring(targetSql.LastIndexOf("FROM [")+("FROM [".Length-1));
        var fromAlias=from.Substring(from.LastIndexOf("AS ")+"AS ".Length);
        var target=targetSql.Substring(0,targetSql.LastIndexOf("FROM ["));
        target=target.Replace("SELECT","INSERT INTO "+from+" (")+")";
        target=target.Replace(fromAlias+".",string.Empty);
        target=Regex.Replace(target,@"\sAS \[[a-zA-z0-9]+\]",string.Empty);
        var insertParams=target.Substring(target.IndexOf('('));
        target = target.Substring(0, target.IndexOf('('));
        var names=Regex.Matches(insertParams,@"\[([a-zA-Z0-9]+)\]");
    
        var remaining=names.Cast<Match>().Select(m=>m.Groups[1].Value).Where(m=>queryProperties.Select(qp=>qp.Name).Contains(m)).ToArray(); //scrape out items that the anonymous select doesn't include a name/value for
         
          //selectAliases[propertyPositions[10]]
          //remaining[10]
        var insertParamsOrdered = remaining.Select((s, i) => new { Position = propertyPositions[i], s })
        .OrderBy(o => o.Position).Select(x => x.s).ToArray();
      var insertParamsDelimited = insertParamsOrdered.Aggregate((s1, s2) => s1 + "," + s2);
      var commandText = target + "(" + insertParamsDelimited + ")" + sql;
        var result=this.ExecuteStoreCommand(commandText,queryParams.Select(qp=>new System.Data.SqlClient.SqlParameter{ ParameterName=qp.Name, Value=qp.Value}).ToArray());
      return result;
}
With some help from http://stackoverflow.com/questions/7808607/how-does-entity-framework-manage-mapping-query-result-to-anonymous-type and http://msdn.microsoft.com/en-us/library/ee358769.aspx


Thursday, May 10, 2012

Clean Repository pattern in EF4


  1. The View
    1. only post the key fields and changed fields
  2. The controller action
    1. transform the post into a DTO and ensure key is set
    2. validate changed fields
  3. The domain layer
    1. accept the DTO and the list of changed properties
    2. validate all business logic
  4. The Service layer
    1. accept the DTO
    2. update ONLY the changed columns.
      1. without fetching a new copy of the object
Goal completed.

1. In the View every input has a wrapper div marked with `data-original="@Model.oldValue"`
OnClick for the save button(s): javascript marks all input fields that have changed to `disabled='disabled'`

2. In the controller action the params required are simply the key fields. I new-up a DTO, and TryUpdateModel on it. For some reason I had to manually set the key property.

3. Domain layer is a simple pass through for now, no business logic is present yet.

4. Service Layer takes in the DTO (as an interface), maps it into a concrete type, and iterates the list of changed property names, setting the ObjectStateManager to modified for each property.

1. For Instance:
 <div class="editor-label">
            @Html.LabelFor(model => model.Threshold3)
        </div>
        <div class="editor-field" data-original="@Model.Threshold3">
            @Html.EditorFor(model => model.Threshold3)
            @Html.ValidationMessageFor(model => model.Threshold3)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>


 and
<script type="text/javascript">
    $(document).ready(function () {
        $('input[type="submit"]').on('click', function () {
            $('.editor-field').each(function (index,e) {
                var input = $('input', e);
                
                var old = $(e).attr('data-original');
                if (typeof (old) !== 'undefined' && old !== false && old == $(input).val()) {
                    input.attr('disabled', 'disabled');
                }
                
            });
        });
    });
    </script>

More after the break...

Friday, January 13, 2012

Stand up Ninject on MVC against EF4

Here's the bindings I used to get it done


    /// <summary>
    
    /// Load your modules or register your 
services here
    
    /// </summary>
    
    /// <param name="kernel">The kernel.</param>
    
private static void RegisterServices(IKernel kernel)
    {
      
  kernel.Bind<string>()
        .ToMethod(f =>
#pragma warning disable 0618 //disable obsolete warning as this is the ONLY place this method should be used in the application
          
  ProjectDb.Adapter.EfExtensions.CreateConnectionString(
#pragma warning restore 0618
          "data source=databaseSource;initial 
catalog=YourDbName;integrated 
security=True;multipleactiveresultsets=True;App=EntityFramework"))
      .Named("efString");

      
kernel.Bind(typeof(Project.Shared.Behaviors.IRepository<>))
  .To<ProjectDb.Adapter.DEVEntities>()
  .InThreadScope()
        
  .WithConstructorArgument("connectionString",kernel.Get<string>("efString"));


    }

Monday, March 21, 2011

EF4 + ESQL for not supported options

So say you want to support wildcard searches against a SQL db. Yes this is probably horrible performance wise, but... useful or as an example how to do ESQL against an EF4 context.

Linqpad against my EF4 context:



  internal IEnumerable<IPlayerFull> SearchForPlayer(string playerNameLike, byte? universeId)
        {

var q= from p in q.Players.Where(p => p.UniverseId == universeId) 
.Where("it.Name like @Name"new ObjectParameter[] { new ObjectParameter("Name", playerNameLike) })
.Where(p => p.UniverseId == universeId)
       orderby p.Name
      select p;
 return q;
bizarre eh? EF4 note, not related to ESQL:

Monday, November 29, 2010

EF4 ToTraceString Method

So if your SoC is just a little muddy on the db-code to query-code like mine appears to be currently you might find yourself wanting to access the ToTraceString() method cleanly on the query side without bringing in EF4 using statements or references.

I accomplished this using the partial class feature.





using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Data.EntityClient;
 
namespace StarfleetCommanderSecure.EF4
{
    public partial class SfeEntities
    {


 public string ToTraceString(IQueryable query)
        {
            var objectQuery = (System.Data.Objects.ObjectQuery) query;
            return objectQuery.ToTraceString();
        }


    }
}

Wednesday, October 20, 2010

EF4 whitelist/explicit eager loading

In linq to sql it was very simple:

    var loadOptions = new System.Data.Linq.DataLoadOptions();
    loadOptions.LoadWith<alliance>(a => a.Players);
    dc.LoadOptions = loadOptions;

where dc was the datacontext.

This specifies that any queries that are submitted to this DataContext that return an Alliance object/entity would also go ahead on the same db trip and fetch it's related Players.

In EF4.0 so far it looks like you have to resort to magic strings. Magic strings are a pet peeve of mine. Your compile time type safety goes out the window in this type of magic string use.

Here are 2 solutions I've found on my googling that can get rid of the magic strings for EF4.

http://j.mp/bub2FW

and

http://j.mp/bKX7X6